diff -u --recursive --new-file v2.0.35/linux/CREDITS linux/CREDITS
--- v2.0.35/linux/CREDITS	Sun Nov 15 10:49:20 1998
+++ linux/CREDITS	Sun Nov 15 10:32:41 1998
@@ -36,10 +36,12 @@
 S: USA
 
 N: Andrea Arcangeli
-E: arcangeli@mbox.queen.it
-W: http://www.cs.unibo.it/~arcangel/
+E: andrea@e-mind.com
+W: http://e-mind.com/~andrea/
 P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC  C0 4B 81 1D 8C 15 C8 E5
 D: Fixed a 2.0.33 mm bug that corrupts memory in linux/mm/vmalloc.c
+D: Author of lil (Linux Interrupt Latency benchmark)
+D: Fixed the shm swap deallocation at swapoff time
 S: Via Ciaclini 26
 S: Imola 40026
 S: Italy
@@ -383,8 +385,8 @@
 W: http://www.pi.se/blox/
 D: Extended support for loadable modules
 D: D-Link pocket adapter drivers
-S: Myrstuguv. 83
-S: S-143 32 VARBY
+S: Grevgatan 11
+S: S-114 53 Stockholm
 S: Sweden
 
 N: Fritz Elfert
@@ -453,15 +455,11 @@
 S: USA
 
 N: Jim Freeman
-E: jfree@caldera.com
+E: jfree@sovereign.org
 W: http://www.sovereign.org/
 D: Initial GPL'd Frame Relay driver
 D: Dynamic PPP devices
 D: Sundry modularizations (PPP, IPX, ...) and fixes
-S: Caldera, Inc.
-S: 240 West Center St.
-S: Orem, Utah 84059-1920
-S: USA
 
 N: Bob Frey
 E: bobf@advansys.com
diff -u --recursive --new-file v2.0.35/linux/Documentation/Changes linux/Documentation/Changes
--- v2.0.35/linux/Documentation/Changes	Mon Jul 13 13:46:24 1998
+++ linux/Documentation/Changes	Sun Nov 15 10:32:41 1998
@@ -17,10 +17,6 @@
 and was originally written and maintained by Alessandro Sigala
 (ssigala@globalnet.it).
 
-   There is now a web page based on this material, thanks to John
-Taylor.  Check out http://www.cviog.uga.edu/LinuxBleed.html if you
-prefer a HTML-ized shopping list.
-
    Para aquellos que prefieran una version en castellano de este
 documento, consultad la traduccion de Alfredo Sanjuan en
 http://slug.ctv.es/~alfredo/Cambios.html (Spanish translation).
diff -u --recursive --new-file v2.0.35/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.0.35/linux/Documentation/Configure.help	Sun Nov 15 10:49:22 1998
+++ linux/Documentation/Configure.help	Sun Nov 15 10:32:42 1998
@@ -12,7 +12,7 @@
 #
 # Information about what a kernel is, what it does, how to patch and
 # compile it and much more is contained in the Kernel-HOWTO, available
-# via ftp (user: anonymous) from sunsite.unc.edu in the directory
+# via FTP (user: anonymous) from sunsite.unc.edu in the directory
 # /pub/Linux/docs/HOWTO.
 #
 # Format of this file: description<nl>variable<nl>helptext<nl><nl>.
@@ -71,7 +71,7 @@
   line option "no387", which comes handy if your coprocessor is
   broken. See the documentation of your boot loader (lilo or loadlin)
   about how to pass options to the kernel at boot time. The lilo
-  procedure is also explained in the SCSI-HOWTO, available via ftp
+  procedure is also explained in the SCSI-HOWTO, available via FTP
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.) This
   means that it is a good idea to say Y here if you intend to use this
   kernel on different machines. More information about the internals
@@ -93,15 +93,15 @@
   Enabling this option will allow you to use a portion of your RAM
   memory as a block device, so that you can make filesystems on it,
   read and write to it and do all the other things that normal block
-  devices (such as harddrives) can do.  It is usually used to load and
+  devices (such as hard drives) can do. It is usually used to load and
   store a copy of a minimal root file system off of a floppy into RAM
-  during the initial install of Linux.  Note that the kernel command
-  line option "ramdisk=XX" is now obsolete.  For details, read
-  Documentation/ramdisk.txt. If you want to compile this as a module (
-  = code which can be inserted in and removed from the running kernel
-  whenever you want), say M and read Documentation/modules.txt.  Most
-  normal users won't need the RAM disk functionality, and can thus say
-  N here.
+  during the initial install of Linux. Note that the kernel command
+  line option "ramdisk=XX" is now obsolete. For details, read
+  Documentation/ramdisk.txt. If you want to compile this as a module
+  ( = code which can be inserted in and removed from the running
+  kernel whenever you want), say M and read Documentation/modules.txt.
+  Most normal users won't need the RAM disk functionality, and can
+  thus say N here.
 
 Initial RAM disk (initrd) support
 CONFIG_BLK_DEV_INITRD
@@ -128,16 +128,16 @@
   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
+  (>540MB) IDE disks, sound card 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
+  select the Old hard disk 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
+Old hard disk (MFM/RLL/IDE) driver
 CONFIG_BLK_DEV_HD_ONLY
   There are two drivers for MFM/RLL/IDE disks.  Most people use the
   newer enhanced driver, but the old one is still around for two
@@ -155,7 +155,7 @@
 CONFIG_BLK_DEV_HD_IDE
   There are two drivers for MFM/RLL/IDE disks.  Most people use just
   the new enhanced driver by itself.  This option installs the old
-  harddisk driver to control the primary IDE/disk interface in the
+  hard disk driver to control the primary IDE/disk interface in the
   system, leaving the new enhanced IDE driver take care of only the
   2nd/3rd/4th IDE interfaces. Doing this will prevent you from having
   an IDE/ATAPI CDROM or tape drive connected to the primary IDE
@@ -176,10 +176,10 @@
   or "hdc", or something similar.
   If this is your only CDROM drive, you can say N to all other CDROM
   options, but be sure to say Y to the ISO9660 filesystem.  Read the
-  CDROM-HOWTO, available via ftp (user: anonymous) in
+  CDROM-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file
   Documentation/cdrom/ide-cd.  Note that older versions of lilo (the
-  linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so
+  Linux boot loader) cannot properly deal with IDE/ATAPI CDROMs, so
   install lilo-16 or higher, available from
   sunsite.unc.edu:/pub/Linux/system/Linux-boot/lilo.
 
@@ -259,7 +259,7 @@
 CONFIG_IDE_CHIPSETS
   Say Y here if you want to include enhanced support for various IDE
   interface chipsets used on motherboards and add-on cards.  This
-  enhanced support may be necessary for linux to be able to access the
+  enhanced support may be necessary for Linux to be able to access the
   3rd/4th drives in some systems.  It may also enable setting of
   higher speed I/O rates to improve system performance with these
   chipsets.  Most of these also require special kernel boot parameters
@@ -311,7 +311,7 @@
   devices are not supported yet.  See the Documentation/ide.txt and
   promise.c files for more info.
 
-XT harddisk support
+XT hard disk support
 CONFIG_BLK_DEV_XD
   Very old 8 bit hard disk controllers used in the IBM XT computer.
   To include a driver for these, say Y.  If you want to compile the
@@ -326,19 +326,19 @@
   your computer's parallel port. Most of them are actually IDE devices
   using a parallel port IDE adapter. This option enables the PARIDE
   subsystem which contains drivers for many of these external drives.
-  Read linux/Documentation/paride.txt for more information.  If you 
-  built PARIDE support into your kernel,  you may still build the 
-  individual protocol modules and high-level drivers as loadable 
-  modules. If you build this support as a module, it will be called 
-  paride.o.  To use the PARIDE support, you must say Y or M here 
-  and also to at least one high-level driver (e.g. "Parallel port 
-  IDE disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI 
-  disks" etc.) and to at least one protocol driver (e.g. "ATEN 
-  EH-100 protocol", "MicroSolutions backpack protocol", "DataStor 
-  Commuter protocol" etc.).
+  Read linux/Documentation/paride.txt for more information.
+  If you built PARIDE support into your kernel, you may still build
+  the individual protocol modules and high-level drivers as loadable
+  modules. If you build this support as a module, it will be called
+  paride.o. To use the PARIDE support, you must say Y or M here and
+  also to at least one high-level driver (e.g. "Parallel port IDE
+  disks", "Parallel port ATAPI CD-ROMs", "Parallel port ATAPI disks"
+  etc.) and to at least one protocol driver (e.g. "ATEN EH-100
+  protocol", "MicroSolutions backpack protocol", "DataStor Commuter
+  protocol" etc.).
 
 Parallel port IDE disks
-CONFIG_PARIDE_PD 
+CONFIG_PARIDE_PD
   This option enables the high-level driver for IDE-type disk devices 
   connected through a parallel port.  If you chose to build PARIDE 
   support into your kernel, you may answer Y here to build in the 
@@ -350,7 +350,7 @@
   hard drives from MicroSolutions.
 
 Parallel port ATAPI CD-ROMs
-CONFIG_PARIDE_PCD 
+CONFIG_PARIDE_PCD
   This option enables the high-level driver for ATAPI CD-ROM devices
   connected through a parallel port. If you chose to build PARIDE
   support into your kernel, you may answer Y here to build in the
@@ -364,7 +364,7 @@
   on CDROMs.
 
 Parallel port ATAPI disks
-CONFIG_PARIDE_PF 
+CONFIG_PARIDE_PF
   This option enables the high-level driver for ATAPI disk devices
   connected through a parallel port.  If you chose to build PARIDE
   support into your kernel, you may answer Y here to build in the
@@ -390,22 +390,22 @@
 CONFIG_PARIDE_PG
   This option enables a special high-level driver for generic ATAPI
   devices connected through a parallel port. The driver allows user
-  programs, such as cdrecord, to send ATAPI commands directly to a
-  device. If you chose to build PARIDE support into your kernel, you 
-  may answer Y here to build in the parallel port generic ATAPI driver, 
-  otherwise you should answer M to build it as a loadable module.
-  The module will be called pg.o.  You must also have at least one 
-  parallel port protocol driver in your system.  This driver 
-  implements an API loosely related to the generic SCSI driver.  
-  See /usr/include/linux/pg.h for details, or visit 
-  http://www.torque.net/parport/cdr.html for more information and
-  the required patches to cdrecord.
+  programs, such as cdrecord, to send ATAPI commands directly to
+  a device.  If you chose to build PARIDE support into your kernel,
+  you may answer Y here to build in the parallel port generic ATAPI
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called pg.o.  You must also have at
+  least one parallel port protocol driver in your system.  This driver
+  implements an API loosely related to the generic SCSI driver.  See
+  /usr/include/linux/pg.h for details.  You can obtain the most recent
+  version of cdrecord from ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ .
+  Versions 1.6.1a3 and later fully support the pg driver.
 
 ATEN EH-100 protocol
 CONFIG_PARIDE_ATEN 
   This option enables support for the ATEN EH-100 parallel port IDE
   protocol.  This protocol is used in some inexpensive low performance 
-  parallel port kits made in Hong Kong. If you chose to build PARIDE 
+  parallel port kits made in Hong Kong.  If you chose to build PARIDE 
   support into your kernel, you may answer Y here to build in the 
   protocol driver, otherwise you should answer M to build it as a 
   loadable module.  The module will be called aten.o.  You must also 
@@ -422,8 +422,8 @@
   a high-level driver for the type of device that you want to support.
 
 DataStor Commuter protocol
-CONFIG_PARIDE_COMM 
-  This option enables support for the Commuter parallel port IDE 
+CONFIG_PARIDE_COMM
+  This option enables support for the Commuter parallel port IDE
   protocol from DataStor.  If you chose to build PARIDE support
   into your kernel, you may answer Y here to build in the protocol
   driver, otherwise you should answer M to build it as a loadable
@@ -431,8 +431,8 @@
   a high-level driver for the type of device that you want to support.
 
 DataStor EP-2000 protocol
-CONFIG_PARIDE_DSTR 
-  This option enables support for the EP-2000 parallel port IDE 
+CONFIG_PARIDE_DSTR
+  This option enables support for the EP-2000 parallel port IDE
   protocol from DataStor.  If you chose to build PARIDE support
   into your kernel, you may answer Y here to build in the protocol
   driver, otherwise you should answer M to build it as a loadable
@@ -440,105 +440,105 @@
   a high-level driver for the type of device that you want to support.
 
 Shuttle EPAT/EPEZ protocol
-CONFIG_PARIDE_EPAT 
-  This option enables support for the EPAT parallel port IDE 
+CONFIG_PARIDE_EPAT
+  This option enables support for the EPAT parallel port IDE
   protocol.  EPAT is a parallel port IDE adapter manufactured by
   Shuttle Technology and widely used in devices from major vendors
-  such as Hewlett-Packard, SyQuest, Imation and Avatar. If you 
-  chose to build PARIDE support into your kernel, you may answer Y 
-  here to build in the protocol driver, otherwise you should answer M 
-  to build it as a loadable module.  The module will be called epat.o.  
-  You must also have a high-level driver for the type of device that 
+  such as Hewlett-Packard, SyQuest, Imation and Avatar.  If you
+  chose to build PARIDE support into your kernel, you may answer Y
+  here to build in the protocol driver, otherwise you should answer M
+  to build it as a loadable module.  The module will be called epat.o.
+  You must also have a high-level driver for the type of device that
   you want to support.
 
 Shuttle EPIA protocol
-CONFIG_PARIDE_EPIA 
-  This option enables support for the (obsolete) EPIA parallel port 
-  IDE protocol from Shuttle Technology.  This adapter can still be found
-  in some no-name kits. If you chose to build PARIDE support into your 
-  kernel, you may answer Y here to build in the protocol driver,
-  otherwise you should answer M to build it as a loadable module.  
-  The module will be called epia.o.  You must also have a high-level 
-  driver for the type of device that you want to support.
+CONFIG_PARIDE_EPIA
+  This option enables support for the (obsolete) EPIA parallel port
+  IDE protocol from Shuttle Technology.  This adapter can still be
+  found in some no-name kits.  If you chose to build PARIDE support
+  into your kernel, you may answer Y here to build in the protocol
+  driver, otherwise you should answer M to build it as a loadable
+  module.  The module will be called epia.o.  You must also have a
+  high-level driver for the type of device that you want to support.
 
 FIT TD-2000 protocol
 CONFIG_PARIDE_FIT2
-  This option enables support for the TD-2000 parallel port IDE protocol
-  from Fidelity International Technology.  This is a simple (low speed) 
-  adapter that is used in some portable hard drives.  If you chose to 
-  build PARIDE support into your kernel, you may answer Y here to 
-  build in the protocol driver, otherwise you should answer M to 
-  build it as a loadable module.  The module will be called fit2.o.
-  You must also have a high-level driver for the type of device 
-  that you want to support.
+  This option enables support for the TD-2000 parallel port IDE
+  protocol from Fidelity International Technology.  This is a simple
+  (low speed) adapter that is used in some portable hard drives.
+  If you chose to build PARIDE support into your kernel, you may
+  answer Y here to build in the protocol driver, otherwise you should
+  answer M to build it as a loadable module.  The module will be
+  called fit2.o.  You must also have a high-level driver for the type
+  of device that you want to support.
 
 FIT TD-3000 protocol
 CONFIG_PARIDE_FIT3
-  This option enables support for the TD-3000 parallel port IDE protocol
-  from Fidelity International Technology. This protocol is used in newer
-  models of their portable disk, CD-ROM and PD/CD devices.   If you chose
-  to build PARIDE support into your kernel, you may answer Y here to
-  build in the protocol driver, otherwise you should answer M to
-  build it as a loadable module.  The module will be called fit3.o.
-  You must also have a high-level driver for the type of device
-  that you want to support.
+  This option enables support for the TD-3000 parallel port IDE
+  protocol from Fidelity International Technology.  This protocol is
+  used in newer models of their portable disk, CD-ROM and PD/CD
+  devices.  If you chose to build PARIDE support into your kernel, you
+  may answer Y here to build in the protocol driver, otherwise you
+  should answer M to build it as a loadable module.  The module will
+  be called fit3.o.  You must also have a high-level driver for the
+  type of device that you want to support.
 
 FreeCom power protocol
-CONFIG_PARIDE_FRPW 
-  This option enables support for the Freecom power parallel port IDE 
-  protocol.  If you chose to build PARIDE support into your kernel, you 
-  may answer Y here to build in the protocol driver, otherwise you 
-  should answer M to build it as a loadable module.  The module will be 
-  called frpw.o.  You must also have a high-level driver for the type 
-  of device that you want to support.
+CONFIG_PARIDE_FRPW
+  This option enables support for the Freecom power parallel port IDE
+  protocol.  If you chose to build PARIDE support into your kernel,
+  you may answer Y here to build in the protocol driver, otherwise you
+  should answer M to build it as a loadable module.  The module will
+  be called frpw.o.  You must also have a high-level driver for the
+  type of device that you want to support.
 
 KingByte KBIC-951A/971A protocols
-CONFIG_PARIDE_KBIC 
-  This option enables support for the KBIC-951A and KBIC-971A parallel 
-  port IDE protocols from KingByte Information Corp.  KingByte's adapters
-  appear in many no-name portable disk and CD-ROM products, especially 
-  in Europe. If you chose to build PARIDE support into your kernel, you 
-  may answer Y here to build in the protocol driver, otherwise you should 
-  answer M to build it as a loadable module.  The module will be called 
-  kbic.o.  You must also have a high-level driver for the type of device 
-  that you want to support.
+CONFIG_PARIDE_KBIC
+  This option enables support for the KBIC-951A and KBIC-971A parallel
+  port IDE protocols from KingByte Information Corp.  KingByte's
+  adapters appear in many no-name portable disk and CD-ROM products,
+  especially in Europe.  If you chose to build PARIDE support into
+  your kernel, you may answer Y here to build in the protocol driver,
+  otherwise you should answer M to build it as a loadable module.
+  The module will be called kbic.o.  You must also have a high-level
+  driver for the type of device that you want to support.
 
 KT PHd protocol
 CONFIG_PARIDE_KTTI
   This option enables support for the "PHd" parallel port IDE protocol
   from KT Technology.  This is a simple (low speed) adapter that is
-  used in some 2.5" portable hard drives.  If you chose to build PARIDE 
-  support into your kernel, you may answer Y here to build in the 
-  protocol driver, otherwise you should answer M to build it as a 
-  loadable module.  The module will be called ktti.o.  You must also 
+  used in some 2.5" portable hard drives.  If you chose to build
+  PARIDE support into your kernel, you may answer Y here to build in
+  the protocol driver, otherwise you should answer M to build it as a
+  loadable module.  The module will be called ktti.o.  You must also
   have a high-level driver for the type of device that you want to
   support.
 
 OnSpec 90c20 protocol
-CONFIG_PARIDE_ON20 
-  This option enables support for the (obsolete) 90c20 parallel port 
+CONFIG_PARIDE_ON20
+  This option enables support for the (obsolete) 90c20 parallel port
   IDE protocol from OnSpec (often marketed under the ValuStore brand
-  name).  If you chose to build PARIDE support into your kernel, you 
-  may answer Y here to build in the protocol driver, otherwise you 
-  should answer M to build it as a loadable module.  The module will 
-  be called on20.o.  You must also have a high-level driver for the 
+  name).  If you chose to build PARIDE support into your kernel, you
+  may answer Y here to build in the protocol driver, otherwise you
+  should answer M to build it as a loadable module.  The module will
+  be called on20.o.  You must also have a high-level driver for the
   type of device that you want to support.
 
 OnSpec 90c26 protocol
-CONFIG_PARIDE_ON26 
-  This option enables support for the 90c26 parallel port IDE protocol 
+CONFIG_PARIDE_ON26
+  This option enables support for the 90c26 parallel port IDE protocol
   from OnSpec Electronics (often marketed under the ValuStore brand
-  name).  If you chose to build PARIDE support into your kernel, you 
-  may answer Y here to build in the protocol driver, otherwise you 
-  should answer M to build it as a loadable module.  The module will 
-  be called on26.o.  You must also have a high-level driver for the 
+  name).  If you chose to build PARIDE support into your kernel, you
+  may answer Y here to build in the protocol driver, otherwise you
+  should answer M to build it as a loadable module.  The module will
+  be called on26.o.  You must also have a high-level driver for the
   type of device that you want to support.
 
 Multiple devices driver support
 CONFIG_BLK_DEV_MD
-  This driver lets you combine several harddisk partitions into one
+  This driver lets you combine several hard disk partitions into one
   logical block device.  Information about how and why to use it and
-  the necessary tools are available over ftp (user: anonymous) from
+  the necessary tools are available over FTP (user: anonymous) from
   sweet-smoke.ufr-info-p7.ibp.fr/pub/public/Linux in the md package
   and the md-FAQ. Please read drivers/block/README.md.  If unsure,
   say N.
@@ -546,7 +546,7 @@
 Linear (append) mode
 CONFIG_MD_LINEAR
   If you enable this, then your multiple devices driver will be able
-  to use the so-called linear mode, i.e. it will combine the harddisk
+  to use the so-called linear mode, i.e. it will combine the hard disk
   partitions by simply appending one to the other. If you want to
   compile this as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
@@ -555,7 +555,7 @@
 RAID-0 (striping) mode
 CONFIG_MD_STRIPED
   If you enable this, then your multiple devices driver will be able
-  to use the so-called raid0 mode, i.e. it will combine the harddisk
+  to use the so-called raid0 mode, i.e. it will combine the hard disk
   partitions into one logical device in such a fashion as to fill them
   up evenly, one chunk here and one chunk there. This will increase
   the throughput rate if the partitions reside on distinct disks.  If
@@ -704,19 +704,19 @@
   this option turned on the TCP/IP stack will use a cryptographic
   challenge protocol known as SYN cookies to enable legitimate users
   to continue to connect, even when your machine is under attack.
-  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 
+  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
-  This is support for floppy drives on Sun Sparc workstations. Say Y
+  This is support for floppy drives on Sun SPARC workstations. Say Y
   if you have a floppy drive, otherwise N. Easy.
 
 Alpha system type
 CONFIG_ALPHA_AVANTI
   Find out what type of Alpha motherboard you have. You will probably
-  want to read the Linux/Alpha homepage on the WWW at
+  want to read the Linux/Alpha home page on the WWW at
   http://www.azstarnet.com/~axplinux/ (To browse the WWW, you need to
   have access to a machine on the Internet that has one of the
   programs lynx, netscape or Mosaic). For this question, it suffices
@@ -760,7 +760,7 @@
   to your kernel during boot time. See the documentation of your boot
   loader (lilo or loadlin) about how to pass options to the kernel.
   The lilo procedure is also explained in the SCSI-HOWTO, available
-  via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
   You also need at least 512kB of RAM cache if you have more than 64MB
   of RAM. Some other things to try when experiencing seemingly random,
   "weird" problems: 1) passing the "no-hlt" option to the kernel
@@ -815,7 +815,7 @@
   http://www.undergrad.math.uwaterloo.ca/~cpbeaure/mca-linux.html on
   the WWW. Note2: some old PCI motherboards have BIOS bugs and may
   crash if "PCI bios support" is enabled (but they run fine without
-  this option). The PCI-HOWTO, available via ftp (user: anonymous) in
+  this option). The PCI-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, contains valuable information
   about which PCI hardware does work under Linux and which doesn't.
   If some of your PCI devices don't work and you get a warning during
@@ -830,7 +830,7 @@
 
 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)
+  If your PCI system uses an IDE hard drive (as opposed to SCSI, say)
   and includes the Intel Triton I/II IDE interface chipset (i82371FB,
   i82371SB or i82371AB), you will want to enable this option to allow
   use of bus-mastering DMA data transfers. Read the comments at the
@@ -841,11 +841,11 @@
 System V IPC
 CONFIG_SYSVIPC
   Inter Process Communication is a suite of library functions and
-  system calls which let processes (= running programs) synchronize
+  system calls which let processes ( = running programs) synchronize
   and exchange information. It is generally considered to be a good
   thing, and some programs won't run unless you enable this. In
   particular, if you want to run the DOS emulator dosemu under Linux
-  (read the DOSEMU-HOWTO, available via ftp (user: anonymous) in
+  (read the DOSEMU-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO), you'll need to say Y here.
   You can find documentation about IPC in ipc.info, which is contained
   in sunsite.unc.edu:/pub/Linux/docs/man/info.tar.gz (extract with
@@ -878,7 +878,7 @@
   and read Documentation/modules.txt. Saying M or N here is dangerous
   because some crucial programs on your system might be in ELF format.
 
-Compile kernel as ELF - if your GCC is ELF-GCC
+Compile kernel as ELF -- if your GCC is ELF-GCC
 CONFIG_KERNEL_ELF
   The gcc version 2.7.0 and newer produces the new ELF binary format
   as default. If you have such a compiler (try "gcc -v"), say Y here,
@@ -919,10 +919,10 @@
   option allows you to run a Java binary just like any other Linux
   program: by typing in its name. As more and more Java programs
   become available, the use for this will gradually increase. You can
-  even execute HTML files containing JAVA applets (= JAVA binaries) if
-  those files start with the string "<!--applet-->". If you want to
+  even execute HTML files containing JAVA applets ( = JAVA binaries)
+  if those files start with the string "<!--applet-->". If you want to
   use this, read Documentation/java.txt and the Java on Linux HOWTO,
-  available via ftp (user: anonymous) at
+  available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. You will then need to install
   the run time system contained in the Java Developers Kit (JDK) as
   described in the HOWTO. If you disable this option it will reduce
@@ -935,16 +935,28 @@
 
 Processor type
 CONFIG_M386
-  This is the processor type of your CPU. It is used for optimizing
-  purposes. In order to compile a kernel that can run on all CPU types
-  (albeit not optimally fast), you can specify "386" here.  If you
-  specify "486" or "Pentium" or "PPro", then the kernel will run on
-  486 and Pentium (=586) and Pentium Pro (=686) CPUs. In rare cases,
-  it can make sense to specify "Pentium" even if running a 486: the
-  kernel will be smaller but slower. On the other hand, if you use a
-  compiler before gcc 2.7 (say "gcc -v" to find out), then you have to
-  say "386" or "486" here even if running on a Pentium or PPro
-  machine. If you don't know what to do, say "386".
+  This is the processor type of your CPU.  It is used for optimizing
+  purposes.  In order to compile a kernel that can run on all CPU
+  types (albeit not optimally fast), you can specify "386" here.  If
+  you specify "486" or "Pentium" or "PPro", then the kernel will run
+  on all of these CPUs: 486 and Pentium (=586) and Pentium Pro (=686).
+  Here are the settings recommended for greatest speed:
+   - "386" for the AMD/Cyrix/Intel 386DX/DXL/SL/SLC/SX and
+     Cyrix/TI 486DLC/DLC2. Only "386" kernels will run on a 386 class
+     machine.
+   - "486" for the AMD/Cyrix/IBM/Intel DX4 or 486DX/DX2/SL/SX/SX2,
+     AMD/Cyrix 5x86, NexGen Nx586 and UMC U5D or U5S
+   - "Pentium" for the AMD K5, K6 and K6-3D, Cyrix MediaGX,
+     Cyrix/IBM/National Semiconductor 6x86 and GXm, IDT Centaur
+     WinChip C6, and Intel Pentium/Pentium MMX
+   - "PPro" for the Cyrix/IBM/National Semiconductor 6x86MX, MII and
+     Intel Pentium II/Pentium Pro
+  In rare cases, it can make sense to specify "Pentium" even if
+  running a 486: the kernel will be smaller but slightly slower.  On
+  the other hand, if you use a compiler before gcc 2.7 (say "gcc -v"
+  to find out), then you have to say "386" or "486" here even if
+  running on a Pentium or PPro machine.
+  If you don't know what to do, say "386".
 
 Compile the kernel into the ELF object format
 CONFIG_ELF_KERNEL
@@ -956,8 +968,8 @@
 
 Is your ELF compiler an extra compiler
 CONFIG_EXTRA_ELF_COMPILER
-  If you have a linuxelf-gcc as opposed to linux-gcc, say Y, otherwise
-  N.
+  If you have a linuxelf-gcc as opposed to linux-gcc, say Y,
+  otherwise N.
 
 Generate little endian code
 CONFIG_CPU_LITTLE_ENDIAN
@@ -1004,7 +1016,7 @@
   and screen blanker programs later on.
   The "kerneld" daemon is included in the package "modules-1.2.8" and
   later. You will probably want to read the kerneld mini-HOWTO,
-  available via ftp (user: anonymous) from
+  available via FTP (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If unsure, say Y.
 
 ARP daemon support (EXPERIMENTAL)
@@ -1036,7 +1048,7 @@
   necessary if you want to use the full power of term (term is a
   program which gives you almost full Internet connectivity if you
   have a regular dial up shell account on some Internet connected Unix
-  computer. Read the Term-HOWTO, available via ftp (user: anonymous)
+  computer. Read the Term-HOWTO, available via FTP (user: anonymous)
   on sunsite.unc.edu:/pub/Linux/docs/HOWTO).  Short answer: say Y.
 
 IP: forwarding/gatewaying
@@ -1045,15 +1057,15 @@
   network (i.e. the computer responsible for distributing Internet
   traffic to and from the machines in the local network and the
   subnetworks) should say Y here (thereby enlarging their kernel by
-  about 5 kB). Note that in this case, you possibly have two ethernet
+  about 5 kB). Note that in this case, you possibly have two Ethernet
   devices in your computer: one for the "outside world" and one for
   your local net. The kernel is not able to recognize both at boot
   time without help; for details read the
-  Multiple-Ethernet-mini-HOWTO, available via ftp (user: anonymous) in
+  Multiple-Ethernet-mini-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.  If your box is
   connected to two networks, it may still make sense to say N here,
   namely if you want to turn your box into a firewall protecting a
-  local network from the internet. The Firewall-HOWTO tells you how to
+  local network from the Internet. The Firewall-HOWTO tells you how to
   do this. If your setup is more complex, say you are connected to
   three networks and you want to act as a firewall between two of them
   and route traffic for the others, you need to say Y here and enable
@@ -1069,7 +1081,7 @@
   (Address Resolution Protocol), explained in the Proxy-Arp mini howto
   on sunsite in /pub/Linux/docs/HOWTO/mini. You also need to say Y
   here if you want to run mrouted in order to do multicast routing as
-  used on the MBONE (a high bandwidth network on top of the internet
+  used on the MBONE (a high bandwidth network on top of the Internet
   which carries audio and video broadcasts) for example. In this case,
   say Y to "IP: multicasting" and "IP: multicast routing" as well. If
   unsure, say N.
@@ -1081,7 +1093,7 @@
   daemon that updates your computer's routing tables, you will need to
   have this option compiled in. You also need multicasting if you
   intend to participate in the MBONE, a high bandwidth network on top
-  of the internet which carries audio and video broadcasts. More
+  of the Internet which carries audio and video broadcasts. More
   information about the MBONE is on the WWW at
   http://www.best.com/~prince/techinfo/mbone.html (to browse the WWW,
   you need to have access to a machine on the Internet that has one of
@@ -1102,7 +1114,7 @@
   If you want to configure your Linux box as a firewall for a local
   TCP/IP based network, say Y here. This will enlarge your kernel by
   about 2kB. You may need to read the FIREWALL-HOWTO, available via
-  ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
   Also, you will need the ipfwadm tool (check the file
   Documentation/Changes for location and latest version) to allow
   selective blocking of internet traffic based on type, origin and
@@ -1174,7 +1186,7 @@
   corresponding local computer. This way, the computers on your local
   net are completely invisible to the outside world, even though they
   can reach the outside and can be reached. This makes it possible to
-  have the computers on the local network participate on the internet
+  have the computers on the local network participate on the Internet
   even if they don't have officially registered IP addresses.  (This
   last problem can also be solved by connecting the Linux box to the
   Internet using SLiRP [SLiRP is a SLIP/PPP emulator that works if you
@@ -1205,12 +1217,12 @@
 
 IP: always defragment
 CONFIG_IP_ALWAYS_DEFRAG
-  This option means that all incoming fragments (= parts of IP packets
-  that arose when some host between origin and destination decided
-  that the IP packets were too large and cut them in pieces) will be
-  reassembled (defragmented) before being processed, even if they are
-  about to be forwarded.  This option is highly recommended if you
-  have enabled the masquerading support (CONFIG_IP_MASQUERADE),
+  This option means that all incoming fragments ( = parts of IP
+  packets that arose when some host between origin and destination
+  decided that the IP packets were too large and cut them in pieces)
+  will be reassembled (defragmented) before being processed, even if
+  they are about to be forwarded.  This option is highly recommended
+  if you have enabled the masquerading support (CONFIG_IP_MASQUERADE),
   because this facility requires that second and further fragments can
   be related to TCP or UDP port numbers, which are only stored in the
   first fragment. When using IP firewall support (CONFIG_IP_FIREWALL),
@@ -1226,15 +1238,15 @@
 IP: aliasing support
 CONFIG_IP_ALIAS
   Sometimes it is useful to give several addresses to a single network
-  interface (= serial port or ethernet card).  The most common case is
-  that you want to serve different WWW documents to the outside
-  according to which of your host names they used to connect to
-  you.  This is explained in detail on the WWW at
+  interface ( = serial port or Ethernet card).  The most common case
+  is that you want to serve different WWW documents to the outside
+  according to which of your host names they used to connect to you.
+  This is explained in detail on the WWW at
   http://www.thesphere.com/~dlp/TwoServers/ (to browse the WWW, you
   need to have access to a machine on the Internet that has one of the
   programs lynx, netscape or Mosaic).  Another scenario would be that
-  there are two logical networks living on your local ethernet and you
-  want to access them both with the same ethernet card.  The
+  there are two logical networks living on your local Ethernet and you
+  want to access them both with the same Ethernet card.  The
   configuration of these alias addresses is done with a special name
   syntax explained in Documentation/networking/alias.txt.  If you want
   this, say Y. Most people don't need it and say N.
@@ -1243,7 +1255,7 @@
 CONFIG_IP_MROUTE
   This is used if you want your machine to act as a router for IP
   packets that have several destination addresses. It is needed on the
-  MBONE, a high bandwidth network on top of the internet which carries
+  MBONE, a high bandwidth network on top of the Internet which carries
   audio and video broadcasts. In order to do that, you would most
   likely run the program mrouted. Information about the multicast
   capabilities of the various network cards is contained in
@@ -1252,17 +1264,17 @@
 
 PC/TCP compatibility mode
 CONFIG_INET_PCTCP
-  If you have been having difficulties telnetting to your Linux machine
-  from a DOS system that uses (broken) PC/TCP networking software (all
-  versions up to OnNet 2.0) over your local ethernet try enabling this
-  option.  Everyone else says N.
+  If you have been having difficulties telnetting to your Linux
+  machine from a DOS system that uses (broken) PC/TCP networking
+  software (all versions up to OnNet 2.0) over your local Ethernet try
+  enabling this option.  Everyone else says N.
   People having problems with NCSA telnet should see the file
   linux/Documentation/networking/ncsa-telnet.
 
 Reverse ARP
 CONFIG_INET_RARP
   Since you asked: if there are (usually diskless or portable)
-  machines on your local network that know their hardware ethernet
+  machines on your local network that know their hardware Ethernet
   addresses but don't know their IP addresses upon startup, they can
   send out a Reverse Address Resolution Protocol (RARP) request to
   find out their own IP addresses. Diskless Sun 3 machines use this
@@ -1304,7 +1316,7 @@
 Disable NAGLE algorithm (normally enabled)
 CONFIG_TCP_NAGLE_OFF
   The NAGLE algorithm works by requiring an acknowledgment before
-  sending small IP frames (= packets).  This keeps tiny telnet and
+  sending small IP frames ( = packets).  This keeps tiny telnet and
   rlogin packets from congesting Wide Area Networks.  Most people
   strongly recommend to say N here, thereby leaving NAGLE enabled.
   Those programs that would benefit from disabling this facility can
@@ -1312,19 +1324,19 @@
 
 IP: Drop source routed frames
 CONFIG_IP_NOSR
-  Usually, the originator of an IP frame (= packet) specifies only the
-  destination, and the hosts along the way do the routing, i.e. they
-  decide how to forward the frame.  However, there is a feature of the
-  IP protocol that allows to specify the full route for a given frame
-  already at its origin. A frame with such a fully specified route is
-  called "source routed".  The question now is whether we should
-  honour these route requests when such frames arrive, or if we should
-  drop all those frames instead.  Honouring them can introduce
-  security problems (and is rarely a useful feature), and hence it is
-  recommended that you say Y here unless you really know what you're
-  doing.
+  Usually, the originator of an IP frame ( = packet) specifies only
+  the destination, and the hosts along the way do the routing, i.e.
+  they decide how to forward the frame.  However, there is a feature
+  of the IP protocol that allows to specify the full route for a given
+  frame already at its origin. A frame with such a fully specified
+  route is called "source routed".  The question now is whether we
+  should honour these route requests when such frames arrive, or if
+  we should drop all those frames instead.  Honouring them can
+  introduce security problems (and is rarely a useful feature), and
+  hence it is recommended that you say Y here unless you really know
+  what you're doing.
 
-IP: Allow large windows (not recommend if <16Mb of memory)
+IP: Allow large windows (not recommend if <16MB of memory)
 CONFIG_SKB_LARGE
   On high speed, long distance networks the performance limit on
   networking becomes the amount of data a machine can buffer until the
@@ -1332,7 +1344,7 @@
   of bits between New York and London...)  This option allows larger
   amounts of data to be "in flight" at a given time.  It also means a
   user process can require a lot more memory for network buffers and
-  thus this option is best only used on machines with 16Mb of
+  thus this option is best only used on machines with 16MB of
   memory or higher.
   Unless you are using long links with end to end speeds of over 2Mbit
   a second or satellite links this option will make no difference to
@@ -1342,13 +1354,13 @@
 CONFIG_IPX
   This is support for the Novell networking protocol, IPX, commonly
   used for local networks of Windows machines. You need it if you want
-  to access Novell Netware file or print servers using the Linux
-  Novell client ncpfs (available via ftp (user: anonymous) from
+  to access Novell NetWare file or print servers using the Linux
+  Novell client ncpfs (available via FTP (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/system/Filesystems/) or from within the
   Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO). In order to do the former,
   you'll also have to say Y to "NCP filesystem support", below. To
-  turn your Linux box into a fully featured Netware file server and
+  turn your Linux box into a fully featured NetWare file server and
   IPX router, say Y here and fetch either lwared from
   sunsite.unc.edu:/pub/Linux/system/Network/daemons/ or mars_nwe from
   ftp.gwdg.de:/pub/linux/misc/ncpfs. For more information, read the
@@ -1376,35 +1388,35 @@
   internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs.
   If you don't know what you are doing, say N.
 
-Appletalk DDP
+AppleTalk DDP
 CONFIG_ATALK
-  Appletalk is the way Apple computers speak to each other on a
-  network.  EtherTalk is the name used for appletalk over ethernet
-  and Localtalk is appletalk over the apple serial links.  If your
-  linux box is connected to such a network and you want to join the
+  AppleTalk is the way Apple computers speak to each other on a
+  network.  EtherTalk is the name used for AppleTalk over Ethernet
+  and LocalTalk is AppleTalk over the Apple serial links.  If your
+  Linux box is connected to such a network and you want to join the
   conversation, say Y.  You will need to use the netatalk package
   so that your Linux box can act as a print and file server for
-  macs as well as access appletalk printers.  Check out
+  macs as well as access AppleTalk printers.  Check out
   http://artoo.hitchcock.org/~flowerpt/projects/linux-netatalk/ on
   the WWW for details (to browse the WWW, you need to have access to
   a machine on the Internet that has one of the programs lynx,
-  netscape or Mosaic).  The NET-2-HOWTO, available via ftp (user:
+  netscape or Mosaic).  The NET-2-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO contains
   valuable information as well.  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.  I hear that the GNU
-  boycott of Apple is over, so even politically correct people are
-  allowed to say Y here.  At the time the kernel is released the
+  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.  I hear that
+  the GNU boycott of Apple is over, so even politically correct people
+  are allowed to say Y here.  At the time the kernel is released the
   localtalk drivers are not yet ready to ship.  The kernel however
   supports localtalk and when such drivers become available all you
-  will need to do is download and install the localtalk driver.  
+  will need to do is download and install the localtalk driver.
 
 Amateur Radio AX.25 Level 2
 CONFIG_AX25
   This is the protocol used for computer communication over amateur
   radio. It is either used by itself for point-to-point links, or to
-  carry other protocols such as tcp/ip. To use it, you need a device
+  carry other protocols such as TCP/IP. To use it, you need a device
   that connects your Linux box to your amateur radio. You can either
   use a low speed TNC (a Terminal Node Controller acts as a kind of
   modem connecting your computer's serial port to your radio's
@@ -1415,7 +1427,7 @@
   own driver) and the other baycom cards (SCC) (supported by the Z8530
   driver).  Information about where to get supporting software for
   Linux amateur radio as well as information about how to configure an
-  AX.25 port is contained in the HAM-HOWTO, available via ftp (user:
+  AX.25 port is contained in the HAM-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. You might also
   want to check out the file Documentation/networking/ax25.txt in the
   kernel source. More information about digital amateur radio in
@@ -1429,7 +1441,7 @@
   NET/ROM is a network layer protocol on top of AX.25 useful for
   routing.  A comprehensive listing of all the software for Linux
   amateur radio users as well as information about how to configure an
-  AX.25 port is contained in the HAM-HOWTO, available via ftp (user:
+  AX.25 port is contained in the HAM-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  You also might
   also want to check out the file Documentation/networking/ax25.txt.
   More information about digital amateur radio in general is on the
@@ -1442,24 +1454,24 @@
 CONFIG_BPQETHER
   AX.25 is the protocol used for computer communication over amateur
   radio. If you say Y here, you will be able to send and receive AX.25
-  traffic over ethernet (also called "BPQ AX.25"), which could be
+  traffic over Ethernet (also called "BPQ AX.25"), which could be
   useful if some other computer on your local network has a direct
   amateur radio connection.
 
 Bridging (EXPERIMENTAL)
 CONFIG_BRIDGE
   If you say Y here, then your Linux box will be able to act as an
-  ethernet bridge, which means that the different ethernet segments it
-  is connected to will appear as one ethernet to the participants.
+  Ethernet bridge, which means that the different Ethernet segments it
+  is connected to will appear as one Ethernet to the participants.
   Several such bridges can work together to create even larger
-  networks of ethernets using the IEEE802.1 spanning tree algorithm.
+  networks of Ethernets using the IEEE802.1 spanning tree algorithm.
   As this is a standard, Linux bridges will interwork properly with
   other third party bridge products. In order to use this, you'll need
-  the bridge configuration tools available via ftp (user: anonymous)
+  the bridge configuration tools available via FTP (user: anonymous)
   from shadow.cabi.net. Note that if your box acts as a bridge, it
-  probably contains several ethernet devices, but the kernel is not
+  probably contains several Ethernet devices, but the kernel is not
   able to recognize more than one at boot time without help; for
-  details read the Multiple-Ethernet-mini-HOWTO, available via ftp
+  details read the Multiple-Ethernet-mini-HOWTO, available via FTP
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
   The Bridging code is still in test. If unsure, say N.
 
@@ -1486,24 +1498,25 @@
 
 SCSI support?
 CONFIG_SCSI
-  If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or
+  If you want to use a SCSI hard disk, SCSI tapedrive, SCSI CDROM or
   any other SCSI device under Linux, say Y and make sure that you know
   the name of your SCSI host adapter (the card inside your computer
   that "speaks" the SCSI protocol), because you will be asked for it.
   You also need to say Y here if you want support for the parallel
   port version of the 100MB IOMEGA ZIP drive.  Please read the
-  SCSI-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:
+  SCSI-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 and Documentation/scsi.txt.
+  ( = 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 and
+  Documentation/scsi.txt.
 
 SCSI disk support
 CONFIG_BLK_DEV_SD
-  If you want to use a SCSI harddisk or the SCSI or parallel port
-  version of the IOMEGA ZIP drive under Linux, say Y and read
-  the SCSI-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for SCSI CDROMs.
+  If you want to use a SCSI hard disk or the SCSI or parallel port
+  version of the IOMEGA ZIP drive under Linux, say Y and read the
+  SCSI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:
+  /pub/Linux/docs/HOWTO. This is NOT for SCSI CDROMs.
   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
@@ -1512,13 +1525,13 @@
 SCSI tape support
 CONFIG_CHR_DEV_ST
   If you want to use a SCSI tapedrive under Linux, say Y and read the
-  SCSI-HOWTO, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO and drivers/scsi/README.st in
-  the kernel source. This is NOT for SCSI CDROMs. 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 and
-  Documentation/scsi.txt .
+  SCSI-HOWTO, available via FTP (user: anonymous) in sunsite.unc.edu:
+  /pub/Linux/docs/HOWTO and drivers/scsi/README.st in the kernel
+  source. This is NOT for SCSI CDROMs. 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 and
+  Documentation/scsi.txt.
 
 SCSI CDROM support
 CONFIG_BLK_DEV_SR
@@ -1533,15 +1546,15 @@
 SCSI generic support
 CONFIG_CHR_DEV_SG
   If you want to use SCSI scanners, synthesizers or CD-writers or just
-  about anything having "SCSI" in its name other than harddisks,
+  about anything having "SCSI" in its name other than hard disks,
   CDROMs or tapes, say Y here. Those won't be supported by the kernel
   directly, so you need some additional software which knows how to
   talk to these devices using the SCSI protocol. For CD-writers, you
-  would need the program cdwrite, available via ftp (user: anonymous)
+  would need the program cdwrite, available via FTP (user: anonymous)
   from sunsite.unc.edu:/pub/Linux/utils/disk-management; for other
   devices, it's possible that you'll have to write the driver software
   yourself, so have a look at the SCSI-HOWTO and at the
-  SCSI-Programming-HOWTO, both available via ftp (user: anonymous) in
+  SCSI-Programming-HOWTO, both available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -1567,27 +1580,26 @@
 CONFIG_SCSI_ADVANSYS
   This is a driver for all SCSI host adapters manufactured by
   AdvanSys. It is documented in the kernel source in
-  drivers/scsi/advansys.c. This driver is also available as a module (
-  = code which can be inserted in and removed from the running kernel
-  whenever you want). If you want to compile it as a module, say M
-  here and read Documentation/modules.txt.
+  drivers/scsi/advansys.c. This driver is also available as a module
+  ( = code which can be inserted in and removed from the running
+  kernel whenever you want). If you want to compile it as a module,
+  say M here and read Documentation/modules.txt.
 
 Adaptec AHA152X/2825 support
 CONFIG_SCSI_AHA152X
   This is support for the AHA-1510, AHA-1520, AHA-1522, and AHA-2825
   SCSI host adapters. It is explained in section 3.3 of the
-  SCSI-HOWTO, available via ftp (user: anonymous) at
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. You might also want to read
-  the comments at the top of drivers/scsi/aha152x.c. This driver is
-  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.
+  SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:
+  /pub/Linux/docs/HOWTO. You might also want to read the comments at
+  the top of drivers/scsi/aha152x.c. This driver is 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.
 
 Adaptec AHA1542 support
 CONFIG_SCSI_AHA1542
   This is support for a SCSI host adapter. It is explained in section
-  3.4 of the SCSI-HOWTO, available via ftp (user: anonymous) at
+  3.4 of the SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Note that Trantor was
   recently purchased by Adaptec, and some former Trantor products are
   being sold under the Adaptec name.  If it doesn't work out of the
@@ -1599,7 +1611,7 @@
 Adaptec AHA1740 support
 CONFIG_SCSI_AHA1740
   This is support for a SCSI host adapter.  It is explained in
-  section 3.5 of the SCSI-HOWTO, available via ftp (user: anonymous)
+  section 3.5 of the SCSI-HOWTO, available via FTP (user: anonymous)
   at sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If it doesn't work out
   of the box, you may have to change some settings in
   drivers/scsi/aha1740.h. This driver is also available as a module
@@ -1667,14 +1679,14 @@
   more time after a bus reset to be ready for the next command, but
   most hard drives and CD-ROM devices are ready in only a few seconds.
   This option has a maximum upper limit of 20 seconds to avoid bad
-  interactions between the aic7xxx driver and the rest of the linux
+  interactions between the aic7xxx driver and the rest of the Linux
   kernel.  The default value has been reduced.  If this doesn't work
   with your hardware, try increasing this value.  Default: 5
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
   This is support for BusLogic MultiMaster and FlashPoint SCSI Host
-  Adapters.  Consult the SCSI-HOWTO, available via anonymous ftp from
+  Adapters.  Consult the SCSI-HOWTO, available via anonymous FTP from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, and the files README.BusLogic
   and README.FlashPoint in drivers/scsi for more information.  If this
   driver does not work correctly without modification, please contact
@@ -1694,21 +1706,21 @@
 DTC3180/3280 SCSI support
 CONFIG_SCSI_DTC3280
   This is support for DTC 3180/3280 SCSI Host Adapters.  Please read
-  the SCSI-HOWTO, available via ftp (user: anonymous) at
+  the SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file
   drivers/scsi/README.dtc3x80.  This driver is also available as a
-  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.
+  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.
 
 EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support
 CONFIG_SCSI_EATA_DMA
   This is support for the EATA-DMA protocol compliant SCSI Host
   Adapters like the SmartCache III/IV, SmartRAID controller families
   and the DPT PM2011B and PM2012B controllers.  Please read the
-  SCSI-HOWTO, available via ftp (user: anonymous) at
+  SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This driver is also
-  available as a module (= code which can be inserted in and removed
+  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.
 
@@ -1717,9 +1729,9 @@
   This driver supports all EATA-PIO protocol compliant SCSI Host
   Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant
   host adapters could also use this driver but are discouraged from
-  doing so, since this driver only supports harddisks and lacks
+  doing so, since this driver only supports hard disks and lacks
   numerous features.  You might want to have a look at the SCSI-HOWTO,
-  available via ftp (user: anonymous) at
+  available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -1731,7 +1743,7 @@
   The source at drivers/scsi/u14-34f.c contains some information about
   this hardware.  If the driver doesn't work out of the box, you may
   have to change some settings in drivers/scsi/u14-34f.c.
-  Read the SCSI-HOWTO, available via ftp (user: anonymous) at
+  Read the SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Note that there is also
   another driver for the same hardware: "UltraStor SCSI support",
   below.  You should enable both only if you want 24F support as well.
@@ -1746,7 +1758,7 @@
   (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX)
   and other adapters based on the Future Domain chipsets (Quantum
   ISA-200S, ISA-250MG; Adaptec AHA-2920; and at least one IBM board).
-  It is explained in section 3.7 of the SCSI-HOWTO, available via ftp
+  It is explained in section 3.7 of the SCSI-HOWTO, available via FTP
   (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
@@ -1757,7 +1769,7 @@
 CONFIG_SCSI_GENERIC_NCR5380
   This is the generic NCR family of SCSI controllers, not to be
   confused with the NCR 53c7 or 8xx controllers. It is explained in
-  section 3.8 of the SCSI-HOWTO, available via ftp (user: anonymous)
+  section 3.8 of the SCSI-HOWTO, available via FTP (user: anonymous)
   at sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If it doesn't work out of
   the box, you may have to change some settings in
   drivers/scsi/g_NCR5380.h. This driver is also available as a module
@@ -1767,7 +1779,7 @@
 
 Enable NCR53c400 extensions
 CONFIG_SCSI_GENERIC_NCR53C400
-  This enables certain optimizations for the NCR53c400 scsi cards. You
+  This enables certain optimizations for the NCR53c400 SCSI cards. You
   might as well try it out. Note that this driver will only probe for
   the Trantor T130B in its default configuration; you might have to
   pass a command line option to the kernel at boot time if it doesn't
@@ -1785,13 +1797,13 @@
 CONFIG_SCSI_NCR53C7xx
   This is the 53c7 and 8xx NCR family of SCSI controllers, not to be
   confused with the NCR 5380 controllers. It is explained in section
-  3.8 of the SCSI-HOWTO, available via ftp (user: anonymous) at
+  3.8 of the SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the
   box, you may have to change some settings in
-  drivers/scsi/53c7,8xx.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.
+  drivers/scsi/53c7,8xx.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.
 
 Always negotiate synchronous transfers
 CONFIG_SCSI_NCR53C7xx_sync
@@ -1819,10 +1831,10 @@
 
 NCR53C8XX SCSI support
 CONFIG_SCSI_NCR53C8XX
-  This is the BSD ncr driver adapted to linux for the NCR53C8XX family
+  This is the BSD ncr driver adapted to Linux for the NCR53C8XX family
   of PCI-SCSI controllers. This driver supports parity checking,
-  tagged command queuing, fast scsi II transfer up to 10 MB/s with
-  narrow scsi devices and 20 MB/s with wide scsi devices.
+  tagged command queuing, fast SCSI II transfer up to 10 MB/s with
+  narrow SCSI devices and 20 MB/s with wide SCSI devices.
   Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875
   controllers has been recently added to the driver.
   Please read drivers/scsi/README.ncr53c8xx for more information.
@@ -1830,11 +1842,11 @@
 
 Synchronous data transfers frequency
 CONFIG_SCSI_NCR53C8XX_SYNC
-  SCSI-2 specifications allow scsi devices to negotiate a synchronous
+  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
+  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.
@@ -1842,7 +1854,7 @@
   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
+  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:
@@ -1861,16 +1873,16 @@
 
 Not allow targets to disconnect
 CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT
-  This option is only provided for safety if you suspect some scsi
+  This option is only provided for safety if you suspect some SCSI
   device of yours to not support properly the target-disconnect
   feature. In that case, you would say Y here. In general however, to
   not allow targets to disconnect is not reasonable if there is more
-  than 1 device on a scsi bus.  The normal answer therefore is N.
+  than 1 device on a SCSI bus.  The normal answer therefore is N.
 
 Enable tagged command queuing
 CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE
   This option allows you to enable tagged command queuing support at
-  linux start-up.  Some scsi devices do not properly support this
+  Linux start-up.  Some SCSI devices do not properly support this
   feature.  The suggested method is to say N here and to use the
   "settags" control command after boot-up to enable this feature:
       echo "settags 2 4" >/proc/scsi/ncr53c8xx/0
@@ -1923,7 +1935,7 @@
   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
+  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
@@ -1942,7 +1954,7 @@
 PAS16 SCSI support
 CONFIG_SCSI_PAS16
   This is support for a SCSI host adapter.  It is explained in section
-  3.10 of the SCSI-HOWTO, available via ftp (user: anonymous) at
+  3.10 of the SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the
   box, you may have to change some settings in drivers/scsi/pas16.h.
 
@@ -1954,7 +1966,7 @@
   does NOT support the PCI version.  The PCI versions are supported by
   the Qlogic ISP driver though.  Information about this driver is
   contained in drivers/scsi/README.qlogicfas.  You should also read
-  the SCSI-HOWTO, available via ftp (user: anonymous) at
+  the SCSI-HOWTO, available via FTP (user: anonymous) at
   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
@@ -1967,7 +1979,7 @@
   card is supported by the "AM53/79C974 PCI SCSI" driver.)  If you
   say Y here, make sure to say Y to "PCI BIOS support" as well.  More
   information is contained in the file drivers/scsi/README.qlogicisp.
-  You should also read the SCSI-HOWTO, available via ftp (user:
+  You should also read the SCSI-HOWTO, available via FTP (user:
   anonymous) at 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
@@ -1978,7 +1990,7 @@
 CONFIG_SCSI_SEAGATE
   These are 8-bit SCSI controllers; the ST-01 is also supported by
   this driver.  It is explained in section 3.9 of the SCSI-HOWTO,
-  available via ftp (user: anonymous) at sunsite.unc.edu:
+  available via FTP (user: anonymous) at sunsite.unc.edu:
   /pub/Linux/docs/HOWTO.  If it doesn't work out of the box, you may
   have to change some settings in drivers/scsi/seagate.h.
   This driver is also available as a module ( = code which can be
@@ -1989,7 +2001,7 @@
 Trantor T128/T128F/T228 SCSI support
 CONFIG_SCSI_T128
   This is support for a SCSI host adapter.  It is explained in section
-  3.11 of the SCSI-HOWTO, available via ftp (user: anonymous) at
+  3.11 of the SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the
   box, you may have to change some settings in drivers/scsi/t128.h.
   Note that Trantor was recently purchased by Adaptec, and some former
@@ -1999,12 +2011,12 @@
 CONFIG_SCSI_ULTRASTOR
   This is support for the UltraStor 14F, 24F and 34F SCSI-2 host
   adapter family.  This driver is explained in section 3.12 of the
-  SCSI-HOWTO, available via ftp (user: anonymous) at
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If it doesn't work out of
-  the box, you may have to change some settings in
-  drivers/scsi/ultrastor.h.  If you want to compile this as a module
-  (= code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read Documentation/modules.txt.
+  SCSI-HOWTO, available via FTP (user: anonymous) at sunsite.unc.edu:
+  /pub/Linux/docs/HOWTO.  If it doesn't work out of the box, you may
+  have to change some settings in drivers/scsi/ultrastor.h.  If you
+  want to compile this as a module ( = code which can be inserted in
+  and removed from the running kernel whenever you want), say M here
+  and read Documentation/modules.txt.
   Note that there is also another driver for UltraStor hardware:
   "UltraStor 14F/34F support", above.
 
@@ -2027,7 +2039,7 @@
   Note that there is also another driver for the same hardware:
   "EATA-DMA support".  You should enable only one of them.
   You want to read the start of drivers/scsi/eata.c and the
-  SCSI-HOWTO, available via ftp (user: anonymous) at
+  SCSI-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -2037,7 +2049,7 @@
 CONFIG_SCSI_NCR53C406A
   This is support for the NCR53c406a SCSI host adapter.  For user
   configurable parameters, check out drivers/scsi/NCR53c406.c in the
-  kernel source.  Also read the SCSI-HOWTO, available via ftp (user:
+  kernel source.  Also read the SCSI-HOWTO, available via FTP (user:
   anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to
   compile this driver as a module ( = code which can be inserted in
   and removed from the running kernel whenever you want), say M here
@@ -2054,7 +2066,7 @@
 CONFIG_SCSI_AM53C974
   This is support for the AM53/79C974 SCSI host adapters. Please read
   drivers/scsi/README.AM53C974 for details. Also, the SCSI-HOWTO,
-  available via ftp (user: anonymous) at
+  available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, is for you.
   Use the native DC390 driver if you've got a Tekram DC390(T) PCI-SCSI
   host adapter.
@@ -2066,7 +2078,7 @@
   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. 
+  it as a module, say M here and read Documentation/modules.txt.
 
 IOMEGA Parallel Port ZIP drive SCSI support
 CONFIG_SCSI_PPA
@@ -2074,7 +2086,7 @@
   (a 100Mb removable media device).  For more information about this
   driver and how to use it you should read the file
   drivers/scsi/README.ppa.  You should also read the SCSI-HOWTO, which
-  is available via anonymous ftp from sunsite.unc.edu in the directory
+  is available via anonymous FTP from sunsite.unc.edu in the directory
   /pub/Linux/docs/HOWTO.  This driver is also available as a module
   which can be inserted in and removed from the running kernel
   whenever you want. If you want to use any two of a parallel port ZIP
@@ -2085,7 +2097,7 @@
   the SCSI version of the ZIP drive: it will be supported
   automatically if you enabled the generic "SCSI disk support", above.
 
-IOMEGA ZIP drive - Buggy EPP chipset support
+IOMEGA ZIP drive -- Buggy EPP chipset support
 CONFIG_SCSI_PPA_HAVE_PEDANTIC
   Contacts with the Iomega driver development team indicate there are
   a few reputably bad EPP implementations in existance. The following
@@ -2099,16 +2111,16 @@
   You can say N here in case you don't intend to connect to any other
   computer at all or all your connections will be either via UUCP
   (UUCP is a protocol to forward mail and news between unix hosts over
-  telephone lines; read the UUCP-HOWTO, available via ftp (user:
+  telephone lines; read the UUCP-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO) or dialing up a
   shell account or a BBS, even using term (term is a program which
   gives you almost full Internet connectivity if you have a regular
   dial up shell account on some Internet connected Unix computer. Read
   the Term-HOWTO).  You'll have to say Y if your computer contains a
-  network card that you want to use under linux (make sure you know
+  network card that you want to use under Linux (make sure you know
   its name because you will be asked for it and read the
   Ethernet-HOWTO; also, if you plan to use more than one network card
-  under linux, read the Multiple-Ethernet-mini-HOWTO, available from
+  under Linux, read the Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you want to use
   SLIP (Serial Line Internet Protocol is the protocol used to send
   Internet traffic over telephone lines or nullmodem cables) or CSLIP
@@ -2124,10 +2136,10 @@
 CONFIG_NET_ETHERNET
   Ethernet is the most common protocol used on Local Area Networks
   (LANs) in universities or companies. 10-base-2 and 10-base-T and
-  100-base-<whatever> are common types of ethernet. If your Linux
-  machine will be connected to an Ethernet and you have an ethernet
+  100-base-<whatever> are common types of Ethernet. If your Linux
+  machine will be connected to an Ethernet and you have an Ethernet
   network card installed in your computer, say Y here and read the
-  Ethernet-HOWTO, available via ftp (user: anonymous) from
+  Ethernet-HOWTO, available via FTP (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Note that the answer to this
   question won't directly affect the kernel: saying N will just cause
   this configure script to skip all the questions about Ethernet
@@ -2140,7 +2152,7 @@
   address.  It is most commonly used in order to make your currently
   inactive SLIP address seem like a real address for local programs.
   If you use SLIP or PPP, you might want to enable it.  Read about it
-  in the Network Administrator's Guide, available via ftp (user:
+  in the Network Administrator's Guide, available via FTP (user:
   anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP.  Since this
   thing comes often handy, the default is Y.  It won't enlarge your
   kernel either.  What a deal.  If you want to compile this as a
@@ -2160,10 +2172,10 @@
   over telephone lines or serial cables (also known as nullmodems).
   Normally, your access provider has to support SLIP in order for you
   to be able to use it, but there is now a SLIP emulator called SLiRP
-  around (available via ftp (user: anonymous) from sunsite.unc.edu:
+  around (available via FTP (user: anonymous) from sunsite.unc.edu:
   /pub/Linux/system/Network/serial/) which allows you to use SLIP over
   a regular dial up shell connection.  If you plan to use SLiRP, make
-  sure to say Y to CSLIP, below.  The NET-2-HOWTO, available via ftp
+  sure to say Y to CSLIP, below.  The NET-2-HOWTO, available via FTP
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains
   how to configure SLIP.  Note that you don't need this option if you
   just want to run term (term is a program which gives you almost full
@@ -2179,13 +2191,13 @@
 CONFIG_SLIP_COMPRESSED
   This protocol is faster than SLIP because it uses compression on the
   TCP/IP headers (not on the data itself), but it has to be supported
-  on both ends. Ask your access provider if you are not sure and say
-  Y, just in case.  You will still be able to use plain SLIP.  If you
-  plan to use SLiRP, the SLIP emulator (available via ftp (user:
+  on both ends.  Ask your access provider if you are not sure and
+  say Y, just in case.  You will still be able to use plain SLIP.  If
+  you plan to use SLiRP, the SLIP emulator (available via FTP (user:
   anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/)
   which allows you to use SLIP over a regular dial up shell
-  connection, you definitely want to say Y here.  The NET-2-HOWTO,
-  available via ftp (user: anonymous) in
+  connection, you definitely want to say Y here.
+  The NET-2-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains how to configure
   CSLIP.  This won't enlarge your kernel.
 
@@ -2208,12 +2220,12 @@
 Radio network interfaces
 CONFIG_NET_RADIO
   Radio based interfaces for Linux.  This includes amateur radio
-  (AX.25), support for wireless ethernet and other systems.  Note that
+  (AX.25), support for wireless Ethernet and other systems.  Note that
   the answer to this question won't directly affect the kernel:
   saying N will just cause this configure script to skip all the
   questions about radio interfaces. Some user-level drivers for scarab
   devices which don't require special kernel support are available via
-  ftp (user: anonymous) from shadow.cabi.net.  If unsure, say N.
+  FTP (user: anonymous) from shadow.cabi.net.  If unsure, say N.
 
 PPP (point-to-point) support
 CONFIG_PPP
@@ -2222,7 +2234,7 @@
   serial) lines.  Ask your access provider if they support it, because
   otherwise you can't use it (not quite true any more: the free
   program SLiRP can emulate a PPP line if you just have a regular dial
-  up shell account on some UNIX computer; get it via ftp (user:
+  up shell account on some UNIX computer; get it via FTP (user:
   anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/).
   To use PPP, you need an additional program called pppd as described
   in Documentation/networking/ppp.txt and in the PPP-HOWTO, available
@@ -2248,7 +2260,7 @@
 CONFIG_PPP_LOTS
   Saying Y here will allow you to have up to 16 PPP connections
   running in parallel.  This is mainly useful if you intend your
-  linux box to act as a dial-in PPP server.  Most people can say N.
+  Linux box to act as a dial-in PPP server.  Most people can say N.
 
 STRIP (Starmode Radio IP) support
 CONFIG_STRIP
@@ -2278,7 +2290,7 @@
   These cards are used to connect your Linux box to an amateur radio
   in order to communicate with other computers.  If you want to use
   this, read Documentation/networking/z8530drv.txt and the HAM-HOWTO,
-  available via ftp (user: anonymous) at
+  available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -2289,9 +2301,9 @@
   This is an experimental driver for Baycom style simple amateur radio
   modems that connect to either a serial interface or a parallel
   interface. The driver supports the ser12 and par96 designs. To
-  configure the driver, use the sethdlc utility available
-  in the standard ax25 utilities package. For information on the modems,
-  see http://www.baycom.de and drivers/net/README.baycom.
+  configure the driver, use the sethdlc utility available in the
+  standard ax25 utilities package. For information on the modems, see
+  http://www.baycom.de and drivers/net/README.baycom.
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. This is recommended.
@@ -2299,18 +2311,16 @@
 Sound card modem driver for AX.25
 CONFIG_SOUNDMODEM
   This experimental driver allows a standard SoundBlaster or
-  WindowsSoundSystem compatible sound card to be used as a packet radio
-  modem (NOT as a telephone modem!), to send digital traffic over
-  amateur radio. 
-
+  WindowsSoundSystem compatible sound card to be used as a packet
+  radio modem (NOT as a telephone modem!), to send digital traffic
+  over amateur radio.
   To configure the driver, use the sethdlc, smdiag and smmixer
   utilities available in the standard ax25 utilities package. For
   information on how to key the transmitter, see
   http://www.ife.ee.ethz.ch/~sailer/pcf/ptt_circ/ptt.html (to browse
   the WWW, you need to have access to a machine on the Internet that
   has a program like lynx or netscape) and
-  Documentation/networking/soundmodem.txt. 
-
+  Documentation/networking/soundmodem.txt.
   If you want to compile this driver as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. This is recommended.
@@ -2322,8 +2332,8 @@
   compatible cards. If you have a dual mode card (i.e. a WSS cards
   with a SoundBlaster emulation) you should say N here and Y to
   "Sound card modem support for WSS and Crystal cards", below, because
-  this usually results in better performance. This option also supports
-  SB16/32/64 in full duplex mode.
+  this usually results in better performance. This option also
+  supports SB16/32/64 in full duplex mode.
 
 Sound card modem support for WSS and Crystal cards
 CONFIG_SOUNDMODEM_WSS
@@ -2337,15 +2347,16 @@
 
 Sound card modem support for 1200 baud AFSK modulation
 CONFIG_SOUNDMODEM_AFSK1200
-  This option enables the soundmodem driver 1200 baud AFSK modem, 
-  compatible to popular modems using TCM3105 or AM7911. The demodulator
-  requires about 12% of the CPU power of a Pentium 75 CPU per channel.
+  This option enables the soundmodem driver 1200 baud AFSK modem,
+  compatible to popular modems using TCM3105 or AM7911.
+  The demodulator requires about 12% of the CPU power of a Pentium 75
+  CPU per channel.
 
 Sound card modem support for 2400 baud AFSK modulation (7.3728MHz crystal)
 CONFIG_SOUNDMODEM_AFSK2400_7
   This option enables the soundmodem driver 2400 baud AFSK modem,
   compatible to TCM3105 modems (over-)clocked with a 7.3728MHz
-  crystal.  Note that the availability of this driver does _not_ imply
+  crystal. Note that the availability of this driver does _not_ imply
   that I recommend building such links. It is only here since users
   especially in eastern Europe have asked me to do so. In fact this
   modulation scheme has many disadvantages, mainly its incompatibility
@@ -2356,8 +2367,8 @@
 CONFIG_SOUNDMODEM_AFSK2400_8
   This option enables the soundmodem driver 2400 baud AFSK modem,
   compatible to TCM3105 modems (over-)clocked with an 8MHz crystal.
-  Note that the availability of this driver does _not_ imply that I
-  recommend building such links. It is only here since users
+  Note that the availability of this driver does _not_ imply that
+  I recommend building such links. It is only here since users
   especially in eastern Europe have asked me to do so. In fact this
   modulation scheme has many disadvantages, mainly its incompatibility
   with many transceiver designs and the fact that the TCM3105 (if
@@ -2405,29 +2416,31 @@
   software installed, e.g. the Crynwr PLIP packet driver
   (http://sunsite.cnam.fr/packages/Telnet/PC/msdos/misc/pktdrvr.txt)
   and winsock or NCSA's telnet.  If you want to use this, say Y and
-  read the PLIP mini-HOWTO, available via ftp (user: anonymous) in
+  read the PLIP mini-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as well as the
   NET-2-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the
   PLIP protocol was changed and this PLIP driver won't work together
   with the PLIP support in Linux versions 1.0.x.  This option enlarges
-  your kernel by about 8kB. If you want to compile this as a module (
-  = code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read Documentation/modules.txt as
-  well as Documentation/networking/net-modules.txt. If you want to use
-  both a parallel printer and PLIP, there are two cases: 1) If the
-  printer and the PLIP cable are to use the same parallel port
-  (presumably because you have just one), it is best to compile both
-  drivers as modules and load and unload them as needed. 2) To use
-  different parallel ports for the printer and the PLIP cable, you can
-  say Y to the printer driver, specify the base address of the
-  parallel port(s) to use for the printer(s) with the "lp" kernel
-  command line option. (See the documentation of your boot loader
-  (lilo or loadlin) about how to pass options to the kernel at boot
-  time. The lilo procedure is also explained in the SCSI-HOWTO,
-  available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses
-  as well as the syntax of the "lp" command line option can be found
-  in drivers/char/lp.c. You can then say Y to this PLIP driver or,
+  your kernel by about 8kB.
+  If you want to compile this as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt.
+  If you want to use both a parallel printer and PLIP, there are two
+  cases:
+  1) If the printer and the PLIP cable are to use the same parallel
+  port (presumably because you have just one), it is best to compile
+  both drivers as modules and load and unload them as needed.
+  2) To use different parallel ports for the printer and the PLIP
+  cable, you can say Y to the printer driver, specify the base address
+  of the parallel port(s) to use for the printer(s) with the "lp"
+  kernel command line option. (See the documentation of your boot
+  loader (lilo or loadlin) about how to pass options to the kernel at
+  boot time. The lilo procedure is also explained in the SCSI-HOWTO,
+  available via FTP (user: anonymous) in sunsite.unc.edu:
+  /pub/Linux/docs/HOWTO.) The standard base addresses as well as the
+  syntax of the "lp" command line option can be found in
+  drivers/char/lp.c. You can then say Y to this PLIP driver or,
   preferably, M in which case Documentation/networking/net-modules.txt
   tells you how to specify the port and IRQ to be used by PLIP at
   module load time.
@@ -2437,8 +2450,8 @@
 CONFIG_EQUALIZER
   If you have two serial connections to some other computer (this
   usually requires two modems and two telephone lines) and you use
-  SLIP (= the protocol for sending internet traffic over telephone
-  lines) or PPP (= a better SLIP) on them, you can make them behave
+  SLIP ( = the protocol for sending internet traffic over telephone
+  lines) or PPP ( = a better SLIP) on them, you can make them behave
   like one double speed connection using this driver. Naturally, this
   has to be supported at the other end as well, either with a similar
   EQL Linux driver or with a Livingston Portmaster 2e. Say Y if you
@@ -2450,7 +2463,7 @@
 Frame Relay (DLCI) support
 CONFIG_DLCI
   This is support for the frame relay protocol; frame relay is a fast
-  low-cost way to connect to a remote internet access provider or to
+  low-cost way to connect to a remote Internet access provider or to
   form a private wide area network. The one physical line from your
   box to the local "switch" (i.e. the entry point to the frame relay
   network, usually at the phone company) can carry several logical
@@ -2491,53 +2504,53 @@
 
 Sun LANCE Ethernet support
 CONFIG_SUN_LANCE
-  This is support for lance ethernet cards on Sun workstations such as
-  the Sparcstation IPC (any Sparc with a network interface 'le0' under
+  This is support for lance Ethernet cards on Sun workstations such as
+  the SPARCstation IPC (any SPARC with a network interface 'le0' under
   SunOS basically).
 
 Sun Intel Ethernet support
 CONFIG_SUN_INTEL
-  This is support for the intel ethernet cards on some Sun
+  This is support for the Intel Ethernet cards on some Sun
   workstations (all those with a network interface 'ie0' under SunOS).
 
 Western Digital/SMC cards
 CONFIG_NET_VENDOR_SMC
-  If you have a network (ethernet) card belonging to this class, say Y
-  and read the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  If you have a network (Ethernet) card belonging to this class, say Y
+  and read the Ethernet-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  one network card under Linux, read the Multiple-Ethernet-mini-HOWTO,
   available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Note that
   the answer to this question doesn't directly affect the kernel:
   saying N will just cause this configure script to skip all the
   questions about Western Digital cards. If you say Y, you will be
-  asked for your specific card in the following questions. If you plan
-  to use more than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
+  asked for your specific card in the following questions.
+  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.
 
 WD80*3 support
 CONFIG_WD80x3
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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 Ultra support
 CONFIG_ULTRA
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  more than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
   Important: There have been many reports that, with some motherboards
@@ -2547,14 +2560,14 @@
 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
+  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
+  more than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
@@ -2563,101 +2576,101 @@
   This is support for the SMC9xxx based Ethernet cards.  Choose this
   option if you have a DELL laptop with the docking station, or
   another SMC9192/9194 based chipset.  Say Y if you want it compiled
-  into the kernel, and read the the file drivers/net/README.smc9 and
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  into the kernel, and read the file drivers/net/README.smc9 and
+  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
+  more than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 AMD LANCE and PCnet (AT1500 and NE2100) support
 CONFIG_LANCE
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  one network card under Linux, read the Multiple-Ethernet-mini-HOWTO,
   available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 3COM cards
 CONFIG_NET_VENDOR_3COM
-  If you have a network (ethernet) card belonging to this class, say Y
-  and read the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  If you have a network (Ethernet) card belonging to this class, say Y
+  and read the Ethernet-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Note that the answer to this
   question doesn't directly affect the kernel: saying N will just
   cause this configure script to skip all the questions about 3COM
   cards. If you say Y, you will be asked for your specific card in the
   following questions. If you plan to use more than one network card
-  under linux, read the Multiple-Ethernet-mini-HOWTO, available from
+  under Linux, read the Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 3c501 support
 CONFIG_EL1
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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.  Also, consider buying a new
   card, since the 3c501 is slow and obsolete.  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,
+  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---and don't use 3c501s.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini -- and don't use 3c501s.
 
 3c503 support
 CONFIG_EL2
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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,
+  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.
 
 3c505 support
 CONFIG_ELPLUS
-  Information about this network (ethernet) card can be found in
+  Information about this network (Ethernet) card can be found in
   Documentation/networking/3c505.txt. If you have a card of this type,
-  say Y and read the Ethernet-HOWTO, available via ftp (user:
+  say Y and read the Ethernet-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to
   compile this as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
   read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
-  If you plan to use more than one network card under linux,
+  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.
 
 3c507 support
 CONFIG_EL16
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
 3c509/3c579 support
 CONFIG_EL3
-  If you have a network (ethernet) card belonging to the 3Com
+  If you have a network (Ethernet) card belonging to the 3Com
   EtherLinkIII series, say Y and read the Ethernet-HOWTO, available
-  via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
+  than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If your card is not
   working you may need to use the DOS setup disk to disable Plug &
@@ -2665,8 +2678,8 @@
 
 3c590 series (592/595/597) "Vortex" support
 CONFIG_VORTEX
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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. More specific information is
   in Documentation/networking/vortex.txt and in the comments at the
   beginning of drivers/net/3c59x.c. If you want to compile this
@@ -2674,23 +2687,23 @@
   running kernel whenever you want), 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
+  than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 Other ISA cards
 CONFIG_NET_ISA
-  If your network (ethernet) card hasn't been mentioned yet and its
+  If your network (Ethernet) card hasn't been mentioned yet and its
   bus system (that's the way the components of the card talk to each
   other) is ISA (as opposed to EISA, VLB or PCI), say Y. Make sure you
   know the name of your card. Read the Ethernet-HOWTO, available via
-  ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If
+  FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If
   unsure, say Y.  Note that the answer to this question doesn't
   directly affect the kernel: saying N will just cause this configure
   script to skip all the remaining ISA network card questions.  If you
   say Y, you will be asked for your specific card in the following
   questions.  If you plan to use more than one network card under
-  linux, read the Multiple-Ethernet-mini-HOWTO, available from
+  Linux, read the Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 ARCnet support
@@ -2698,19 +2711,19 @@
   If you have a network card of this type, say Y and check out the
   (arguably) beautiful poetry in Documentation/networking/arcnet.txt.
   You might also want to have a look at the Ethernet-HOWTO, available
-  via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO
-  (even though ARCnet is not really ethernet).  This driver is also
+  via FTP (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO
+  (even though ARCnet is not really Ethernet).  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
+  use more than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 Enable arc0e (ARCnet "ether-encap" packet format)
 CONFIG_ARCNET_ETH
-  This allows you to use "ethernet encapsulation" with your ARCnet
+  This allows you to use "Ethernet encapsulation" with your ARCnet
   card via the virtual arc0e device.  You only need arc0e if you want
   to talk to nonstandard ARCnet software, specifically,
   DOS/Windows-style "NDIS" drivers.  You do not need to enable this
@@ -2736,77 +2749,77 @@
 
 Cabletron E21xx support
 CONFIG_E2100
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
 DEPCA support
 CONFIG_DEPCA
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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 as well as
-  drivers/net/depca.c.  If you want to compile this as a module ( =
-  code which can be inserted in and removed from the running kernel
+  drivers/net/depca.c.  If you want to compile this as a module
+  (= code which can be inserted in and removed from the running kernel
   whenever you want), say M here and read Documentation/modules.txt as
   well as Documentation/networking/net-modules.txt. If you plan to use
-  more than one network card under linux, read the
+  more than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 EtherWorks 3 support
 CONFIG_EWRK3
-  This driver supports the DE203, DE204 and DE205 network (ethernet)
+  This driver supports the DE203, DE204 and DE205 network (Ethernet)
   cards. If this is for you, say Y and read drivers/net/README.ewrk3
   in the kernel source as well as the Ethernet-HOWTO, available via
-  ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
+  FTP (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO.
   If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
+  than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 SEEQ8005 support
 CONFIG_SEEQ8005
-  This is a driver for the SEEQ 8005 network (ethernet) card. If this
-  is for you, read the Ethernet-HOWTO, available via ftp (user:
+  This is a driver for the SEEQ 8005 network (Ethernet) card. If this
+  is for you, read the Ethernet-HOWTO, available via FTP (user:
   anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan
-  to use more than one network card under linux, read the
+  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.
 
 AT1700 support
 CONFIG_AT1700
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
 FMV-181/182/183/184 support
 CONFIG_FMV18X
-  If you have a Fujitsu FMV-181/182/183/184 network (ethernet) card,
-  say Y and read the Ethernet-HOWTO, available via ftp (user:
+  If you have a Fujitsu FMV-181/182/183/184 network (Ethernet) card,
+  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,
+  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.
   If you use FMV-183 or FMV-184 and it is not working, you may need
@@ -2820,21 +2833,21 @@
 
 EtherExpressPro support
 CONFIG_EEXPRESS_PRO
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
 EtherExpress support
 CONFIG_EEXPRESS
-  If you have an EtherExpress16 network (ethernet) card, say Y and
-  read the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  If you have an EtherExpress16 network (Ethernet) card, say Y and
+  read the Ethernet-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the Intel
   EtherExpress16 card used to be regarded as a very poor choice
   because the driver was very unreliable. We now have a new driver
@@ -2843,24 +2856,24 @@
   running kernel whenever you want), 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
+  than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 NI5210 support
 CONFIG_NI52
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  one network card under Linux, read the Multiple-Ethernet-mini-HOWTO,
   available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 NI6510 support
 CONFIG_NI65
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  one network card under Linux, read the Multiple-Ethernet-mini-HOWTO,
   available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 Ottawa PI and PI/2 support
@@ -2871,7 +2884,7 @@
   http://hydra.carleton.ca/info/pi2.html (To browse the WWW, you need
   to have access to a machine on the Internet that has one of the
   programs lynx, netscape or Mosaic). If you have one of these cards,
-  you can say Y here and should read the HAM-HOWTO, available via ftp
+  you can say Y here and should read the HAM-HOWTO, available via FTP
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also,
   you should have said Y to "AX.25 support" above, because AX.25 is
   the protocol used for digital traffic over radio links.
@@ -2888,7 +2901,7 @@
 AT&T WaveLAN & DEC RoamAbout DS support
 CONFIG_WAVELAN
     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.
+  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
@@ -2902,129 +2915,129 @@
 
 HP PCLAN+ (27247B and 27252A) support
 CONFIG_HPLAN_PLUS
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
 HP PCLAN (27245 and other 27xxx series) support
 CONFIG_HPLAN
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
 HP 10/100VG PCLAN (ISA, EISA, PCI) support
 CONFIG_HP100
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
   Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
+  than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 NE2000/NE1000 support
 CONFIG_NE2000
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
 SK_G16 support
 CONFIG_SK_G16
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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. If you plan to use more than
-  one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
+  one network card under Linux, read the Multiple-Ethernet-mini-HOWTO,
   available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 EISA, VLB, PCI and on board controllers
 CONFIG_NET_EISA
   This is another class of network cards which attach directly to the
   bus. If you have one of those, say Y and read the Ethernet-HOWTO,
-  available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO; if you are unsure, say
-  Y. Note that the answer to this question doesn't directly affect the
+  available via FTP (user: anonymous) from sunsite.unc.edu:
+  /pub/Linux/docs/HOWTO; if you are unsure, say Y.
+  Note that the answer to this question doesn't directly affect the
   kernel: saying N will just cause this configure script to skip all
   the questions about this class of network cards. If you say Y, you
-  will be asked for your specific card in the following questions.  If
-  you plan to use more than one network card under linux, read the
+  will be asked for your specific card in the following questions.
+  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.
 
 Ansel Communications EISA 3200 support
 CONFIG_AC3200
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
-Apricot Xen-II on board ethernet
+Apricot Xen-II on board Ethernet
 CONFIG_APRICOT
-  If you have a network (ethernet) controller of this type, say Y and
-  read the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  If you have a network (Ethernet) controller of this type, say Y and
+  read the Ethernet-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
   Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
+  than one network card under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 DE425, DE434, DE435 support
 CONFIG_DE4X5
-  This is support for the DIGITAL series of PCI/EISA ethernet
+  This is support for the DIGITAL series of PCI/EISA Ethernet
   cards. These include the DE425, DE434, DE435, DE450 and DE500
   models. If you have a network card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  the Ethernet-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  More specific information is
   contained in drivers/net/README.de4x5. 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
+  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.
 
 DECchip Tulip (dc21x4x) PCI support
 CONFIG_DEC_ELCP
-  This driver is developed for the SMC EtherPower series ethernet
+  This driver is developed for the SMC EtherPower series Ethernet
   cards and also works with cards based on the DECchip
   21040/21041/21140 (Tulip series) chips. (If your card is NOT SMC
   EtherPower 10/100 PCI (smc9332dst), you can also try the driver from
   "DE425, DE434, DE435 support", above.) However, most people with a
   network card of this type will say Y here. Do read the
-  Ethernet-HOWTO, available via ftp (user: anonymous) in
+  Ethernet-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is
   contained in Documentation/networking/tulip.txt. This driver is also
   available as a module ( = code which can be inserted in and removed
@@ -3035,33 +3048,33 @@
 Digi Intl. RightSwitch support
 CONFIG_DGRS
   This is support for the Digi International RightSwitch series of
-  PCI/EISA ethernet switch cards.  These include the SE-4 and the SE-6
+  PCI/EISA Ethernet switch cards.  These include the SE-4 and the SE-6
   models. If you have a network card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  the Ethernet-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  More specific information is
   contained in drivers/net/README.dgrs. 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
+  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.
 
 ICL EtherTeam 16i/32 support
 CONFIG_ETH16I
-  If you have a network (ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  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
+  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.
 
-TI ThunderLAN support (EXPERIMENTAL)
+TI ThunderLAN support
 CONFIG_TLAN
   If you have a TLAN based network card which is supported by this
   driver, say Y and read the Ethernet-HOWTO.  Devices currently
@@ -3069,19 +3082,25 @@
   Internal NetFlex 3.  This driver is also available as a module.
   Please email feedback to james.banks@caldera.com.
 
+VIA Rhine support
+CONFIG_VIA_RHINE
+  If you have a VIA "rhine" based network card (Rhine-I (3043) or
+  Rhine-2 (VT86c100A)), say Y here. To build this driver as a module
+  say M.
+
 Zenith Z-Note support
 CONFIG_ZNET
   The Zenith Z-Note notebook computer has a built-in network
-  (ethernet) card, and this is the Linux driver for it. Note that the
+  (Ethernet) card, and this is the Linux driver for it. Note that the
   IBM Thinkpad 300 is compatible with the Z-Note and is also supported
-  by this driver. Read the Ethernet-HOWTO, available via ftp (user:
+  by this driver. Read the Ethernet-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 Pocket and portable adapters
 CONFIG_NET_POCKET
-  Cute little network (ethernet) devices which attach to the parallel
+  Cute little network (Ethernet) devices which attach to the parallel
   port ("pocket adapters"), commonly used with laptops. If you have
-  one of those, say Y and read the Ethernet-HOWTO, available via ftp
+  one of those, say Y and read the Ethernet-HOWTO, available via FTP
   (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you
   want to plug a network card into the PCMCIA slot of your laptop
   instead (PCMCIA is the standard for credit card size extension cards
@@ -3091,7 +3110,7 @@
   will just cause this configure script to skip all the questions
   about this class of network devices.  If you say Y, you will be
   asked for your specific device in the following questions.  If you
-  plan to use more than one network device under linux, read the
+  plan to use more than one network device under Linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use an
   adapter attaching to the parallel port as well as a parallel
@@ -3099,11 +3118,11 @@
 
 AT-LAN-TEC/RealTek pocket adapter support
 CONFIG_ATP
-  This is a network (ethernet) device which attaches to your parallel
+  This is a network (Ethernet) device which attaches to your parallel
   port. Read drivers/net/atp.c as well as the Ethernet-HOWTO,
-  available via ftp (user: anonymous) from
+  available via FTP (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. If
-  you plan to use more than one network card under linux, read the
+  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. If you intend to use
   this driver, you should have said N to the Parallel Printer support,
@@ -3111,36 +3130,36 @@
 
 D-Link DE600 pocket adapter support
 CONFIG_DE600
-  This is a network (ethernet) device which attaches to your parallel
+  This is a network (Ethernet) device which attaches to your parallel
   port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO,
-  available via ftp (user: anonymous) from
+  available via FTP (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this.  If
   you want to compile this as a module ( = code which can be inserted
   in and removed from the running kernel whenever you want), say M
   here and read Documentation/modules.txt. If you intend to use this
   pocket adapter as well as a parallel printer, you should compile
   both drivers as modules. If you plan to use more than one network
-  card under linux, read the Multiple-Ethernet-mini-HOWTO, available
+  card under Linux, read the Multiple-Ethernet-mini-HOWTO, available
   from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 D-Link DE620 pocket adapter support
 CONFIG_DE620
-  This is a network (ethernet) device which attaches to your parallel
+  This is a network (Ethernet) device which attaches to your parallel
   port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO,
-  available via ftp (user: anonymous) from
+  available via FTP (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this.  If
   you want to compile this as a module ( = code which can be inserted
   in and removed from the running kernel whenever you want), say M
   here and read Documentation/modules.txt. If you intend to use this
   pocket adapter as well as a parallel printer, you should compile
   both drivers as modules. If you plan to use more than one network
-  card under linux, read the Multiple-Ethernet-mini-HOWTO, available
+  card under Linux, read the Multiple-Ethernet-mini-HOWTO, available
   from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 Token Ring driver support
 CONFIG_TR
   Token Ring is IBM's way of communication on a local network; the
-  rest of the world uses ethernet. If you are connected to a token
+  rest of the world uses Ethernet. If you are connected to a token
   ring network and want to use your Token Ring card under Linux,
   say Y.  Most people can say N here.
 
@@ -3157,7 +3176,7 @@
 Support non-SCSI/IDE/ATAPI drives
 CONFIG_CD_NO_IDESCSI
   If you have a CDROM drive that is neither SCSI nor IDE/ATAPI, say Y
-  here, otherwise N. Read the CDROM-HOWTO, available via ftp (user:
+  here, otherwise N. Read the CDROM-HOWTO, available via FTP (user:
   anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the
   answer to this question doesn't directly affect the kernel: saying N
   will just cause this configure script to skip all the questions
@@ -3248,8 +3267,8 @@
 CONFIG_AZTCD
   This is your driver if you have an Aztech CDA268-01A, Orchid
   CD-3110, Okano or Wearnes CDD110, Conrad TXC, or CyCDROM CR520 or
-  CR540 CDROM drive.  This driver - just like all these CDROM drivers
-  - is NOT for CDROM drives with IDE/ATAPI interface, such as Aztech
+  CR540 CDROM drive. This driver -- just like all these CDROM drivers
+  -- is NOT for CDROM drives with IDE/ATAPI interface, such as Aztech
   CDA269-031SE.
 
 Sony CDU535 CDROM support
@@ -3298,8 +3317,8 @@
   at boot time, please say Y. Boot time command line options (or
   'append=' options in /etc/lilo.conf) are:
                 isp16=<port>,<irq>,<dma>,<drive_type>
-  Here 'port','irq' and 'dma' are the base i/o address, irq number and
-  dma line assumed to be used by the attached cdrom
+  Here 'port','irq' and 'dma' are the base i/o address, IRQ number and
+  DMA line assumed to be used by the attached cdrom
   drive. 'drive_type' is the type of cdrom drive or its emulation
   mode. Valid values for drive_type include: Sanyo, Panasonic (same as
   Sanyo), Sony and Mitsumi.  Default values are: port=0x340, irq=0,
@@ -3316,8 +3335,8 @@
   usage (also called diskquotas). Currently, it works only for the
   ext2 filesystem. You need additional software in order to use quota
   support; check the file Documentation/Changes for that. Probably the
-  quota support is only useful for multi user systems. If unsure, say
-  N.
+  quota support is only useful for multi user systems. If unsure,
+  say N.
 
 Mandatory lock support
 CONFIG_LOCK_MANDATORY
@@ -3333,56 +3352,56 @@
 
 Minix fs support
 CONFIG_MINIX_FS
-  Minix is a simple operating system used in many classes about
-  OS's. The minix filesystem (= method to organize files on a harddisk
+  Minix is a simple operating system used in many classes about OS's.
+  The minix filesystem ( = method to organize files on a hard disk
   partition or a floppy disk) was the original filesystem for Linux,
   has been superseded by the second extended filesystem ext2fs but is
   still used for root/boot and other floppies or ram disks since it is
-  leaner. You don't want to use it on your harddisk because of certain
-  built-in restrictions. This option will enlarge your kernel by about
-  25 kB. Everyone should say Y or M so that they are able to read this
-  common floppy format.  If you want to compile this as a module
-  ( = code which can be inserted in and removed from the
+  leaner. You don't want to use it on your hard disk because of
+  certain built-in restrictions. This option will enlarge your kernel
+  by about 25 kB. Everyone should say Y or M so that they are able to
+  read this common floppy format. If you want to compile this as
+  a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
   Documentation/modules.txt. Note that the filesystem of your root
   partition cannot be compiled as a module.
 
 Extended fs support
 CONFIG_EXT_FS
-  This is the old Linux filesystem (= method to organize files on a
-  harddisk partition or a floppy disk) and not in use anymore. It
-  enlarges your kernel by about 25 kB. Let's all kill this beast. Say
-  N.
+  This is the old Linux filesystem ( = method to organize files on a
+  hard disk partition or a floppy disk) and not in use anymore.
+  It enlarges your kernel by about 25 kB. Let's all kill this beast.
+  Say N.
 
 Second extended fs support
 CONFIG_EXT2_FS
-  This is the de facto standard Linux filesystem (= method to organize
-  files on a storage device) for harddisks. You want to say Y, unless
-  you intend to use Linux exclusively from inside a DOS partition
-  using the umsdos filesystem. The advantage of the latter is that you
-  can get away without repartitioning your hard drive (which often
-  implies backing everything up and restoring afterwards); the
-  disadvantage is that Linux becomes susceptible to DOS viruses and
-  that umsdos is somewhat slower than ext2fs. Even if you want to run
-  Linux in this fashion, it might be a good idea to have ext2fs
-  around: it enables you to read more floppy disks and facilitates the
-  transition to a *real* Linux partition later. Another (rare) case
-  which doesn't require ext2fs is a diskless Linux box which mounts
-  all files over the network using NFS (in this case it's sufficient
-  to enable NFS filesystem support below; if you are planning to do
-  this, have a look at the netboot package in
-  /pub/Linux/system/Linux-boot/, available via ftp (user: anonymous)
+  This is the de facto standard Linux filesystem ( = method to
+  organize files on a storage device) for hard disks. You want to
+  say Y, unless you intend to use Linux exclusively from inside a DOS
+  partition using the umsdos filesystem. The advantage of the latter
+  is that you can get away without repartitioning your hard drive
+  (which often implies backing everything up and restoring
+  afterwards); the disadvantage is that Linux becomes susceptible to
+  DOS viruses and that umsdos is somewhat slower than ext2fs. Even if
+  you want to run Linux in this fashion, it might be a good idea to
+  have ext2fs around: it enables you to read more floppy disks and
+  facilitates the transition to a *real* Linux partition later.
+  Another (rare) case which doesn't require ext2fs is a diskless Linux
+  box which mounts all files over the network using NFS (in this case
+  it's sufficient to enable NFS filesystem support below; if you are
+  planning to do this, have a look at the netboot package in
+  /pub/Linux/system/Linux-boot/, available via FTP (user: anonymous)
   from sunsite.unc.edu, extract with "tar xzvf filename"). There is a
-  short ext2fs-FAQ, available via ftp (user: anonymous) in
+  short ext2fs-FAQ, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/faqs. This option will enlarge your
   kernel by about 41 kB. Default is Y.
 
 xiafs filesystem support
 CONFIG_XIA_FS
-  This is an old filesystem (= method to organize files on a harddisk
-  partition or a floppy disk) and not in use anymore. This option
+  This is an old filesystem ( = method to organize files on a hard
+  disk partition or a floppy disk) and not in use anymore. This option
   would enlarge your kernel by about 28 kB. Let's all kill this beast:
-  say N.  If you want to compile this as a module ( = code which can
+  say N. If you want to compile this as a module ( = code which can
   be inserted in and removed from the running kernel whenever you
   want), say M here and read Documentation/modules.txt. Note that the
   filesystem of your root partition cannot be compiled as a module.
@@ -3397,16 +3416,16 @@
   this as a module however ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
   read Documentation/modules.txt. Note that if you compile the FAT
-  support as a module, you cannot compile any of the FAT-based file-
-  systems into the kernel - they will have to be modules as well.
+  support as a module, you cannot compile any of the FAT-based
+  filesystems into the kernel -- they will have to be modules as well.
   The filesystem of your root partition cannot be a module.
 
 msdos fs support
 CONFIG_MSDOS_FS
-  This allows you to mount MSDOS partitions of your harddrive (unless
+  This allows you to mount MSDOS partitions of your hard drive (unless
   they are compressed; to access compressed MSDOS partitions under
   Linux, you can either use the DOS emulator DOSEMU, described in the
-  DOSEMU-HOWTO, available via ftp (user: anonymous) at
+  DOSEMU-HOWTO, available via FTP (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, or try dmsdosfs in
   sunsite.unc.edu:/pub/Linux/system/Filesystems/dosfs. If you intend
   to use dosemu with a non-compressed MSDOS partition, say Y here) and
@@ -3430,7 +3449,7 @@
 
 vfat fs support
 CONFIG_VFAT_FS
-  This allows you to mount MSDOS partitions of your harddrive. It
+  This allows you to mount MSDOS partitions of your hard drive. It
   will let you use filenames in a way compatible with the long
   filenames used by Windows'95 and Windows NT fat-based (not NTFS)
   partitions. It does not support Windows'95 compressed filesystems.
@@ -3445,7 +3464,7 @@
 umsdos: Unix like fs on top of std MSDOS fs
 CONFIG_UMSDOS_FS
   Say Y here if you want to run Linux from within an existing DOS
-  partition of your harddrive. The advantage of this is that you can
+  partition of your hard drive. The advantage of this is that you can
   get away without repartitioning your hard drive (which often implies
   backing everything up and restoring afterwards) and hence you're
   able to quickly try out Linux or show it to your friends; the
@@ -3459,9 +3478,8 @@
   enabled both "fat fs support" and "msdos fs support" above. If
   unsure, say N.  If you want to compile this as a module ( = code
   which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read
-  Documentation/modules.txt. Note that the filesystem of your root
-  partition cannot be a module.
+  whenever you want), say M here and read Documentation/modules.txt.
+  Note that the filesystem of your root partition cannot be a module.
 
 nls: Native language codepages and Unicode support
 CONFIG_NLS
@@ -3557,7 +3575,7 @@
 
 nls codepage 850
 CONFIG_NLS_CODEPAGE_850
-  This is the DOS codepage that is used in much of Europe--United
+  This is the DOS codepage that is used in much of Europe -- United
   Kingdom, Germany, Spain, Italy, and [add more countries here].
   It has some characters useful to many European languages that are
   not part of codepage 437.
@@ -3619,49 +3637,48 @@
 CONFIG_PROC_FS
   This is a virtual filesystem providing information about the status
   of the system. "Virtual" means that it doesn't take up any space on
-  your harddisk: the files are created on the fly when you access
+  your hard disk: the files are created on the fly when you access
   them. Also, you cannot read the files with less: you need to use
   more or cat. The filesystem is explained in the Kernel Hacker's
-  Guide, available via ftp (user: anonymous) in
+  Guide, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/LDP and also on the proc(8) manpage
   ("man 8 proc").  This option will enlarge your kernel by about 18
   kB. It's totally cool; for example, "cat /proc/interrupts" gives
   information about what the different IRQs are used for at the moment
   (there is a small number of Interrupt ReQuest lines in your computer
-  that are used by the periphery to gain the CPU's attention - often a
-  source of trouble if two devices are mistakenly configured to use
+  that are used by the periphery to gain the CPU's attention -- often
+  a source of trouble if two devices are mistakenly configured to use
   the same IRQ). Several programs depend on this, so everyone should
   say Y here.
 
 NFS filesystem support
 CONFIG_NFS_FS
   If you are connected to some other (usually local) Unix computer
-  (using SLIP, PLIP, PPP or ethernet) and want to mount files
+  (using SLIP, PLIP, PPP or Ethernet) and want to mount files
   residing on that computer (the NFS server) using the Network
   File Sharing protocol, say Y. "Mounting files" means that the client
   can access the files with usual UNIX commands as if they were
-  sitting on the client's harddisk. For this to work, the server must
+  sitting on the client's hard disk. For this to work, the server must
   run the programs nfsd and mountd (but does not need to have NFS
   filesystem support enabled). NFS is explained in the Network
-  Administrator's Guide, available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man page: "man
-  nfs". There is also a NFS-FAQ in
-  sunsite.unc.edu:/pub/Linux/docs/faqs which presumes that you know
-  the basics of NFS already. If you say Y here, you should have said Y
-  to TCP/IP networking also. This option would enlarge your kernel by
-  about 27 kB. This filesystem 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. If you configure a diskless
-  machine which will mount its root filesystem over nfs, you cannot
-  compile this driver as a module. If you don't know what all this is
-  about, say N.
+  Administrator's Guide, available via FTP (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man page: "man nfs".
+  There is also a NFS-FAQ in sunsite.unc.edu:/pub/Linux/docs/faqs
+  which presumes that you know the basics of NFS already. If you say Y
+  here, you should have said Y to TCP/IP networking also. This option
+  would enlarge your kernel by about 27 kB. This filesystem 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.
+  If you configure a diskless machine which will mount its root
+  filesystem over NFS, you cannot compile this driver as a module.
+  If you don't know what all this is about, say N.
 
 Root file system on NFS
 CONFIG_ROOT_NFS
   If you want your Linux box to mount its whole root filesystem from
   some other computer over the net via NFS (presumably because your
-  box doesn't have a harddisk), say Y. Read Documentation/nfsroot.txt
+  box doesn't have a hard disk), say Y. Read Documentation/nfsroot.txt
   for details. Most people say N here.
 
 BOOTP support
@@ -3695,19 +3712,19 @@
   Unix filenames are also supported by this driver. If you have a
   CDROM drive and want to do more with it than just listen to audio
   CDs and watch its LEDs, say Y (and read the CDROM-HOWTO, available
-  via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your
-  kernel by about 27 kB; otherwise say N.  If you want to compile this
-  as a module ( = code which can be inserted in and removed from the
-  running kernel whenever you want), say M here and read
+  via FTP (user: anonymous) from sunsite.unc.edu:
+  /pub/Linux/docs/HOWTO), thereby enlarging your kernel by about
+  27 kB; otherwise say N. If you want to compile this as a module
+  ( = code which can be inserted in and removed from the running
+  kernel whenever you want), say M here and read
   Documentation/modules.txt.
 
 OS/2 HPFS filesystem support (read only)
 CONFIG_HPFS_FS
   OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS
-  is the filesystem used for organizing files on OS/2 harddisk
+  is the filesystem used for organizing files on OS/2 hard disk
   partitions. Say Y if you want to be able to read files from an OS/2
-  HPFS partition of your harddrive. OS/2 floppies however are in
+  HPFS partition of your hard drive. OS/2 floppies however are in
   regular MSDOS format, so you don't need this option in order to be
   able to read them. Read Documentation/filesystems/hpfs.txt. This
   filesystem is also available as a module ( = code which can be
@@ -3717,10 +3734,10 @@
 
 System V and Coherent filesystem support
 CONFIG_SYSV_FS
-  SCO, Xenix and Coherent are commercial Unix systems for intel
+  SCO, Xenix and Coherent are commercial Unix systems for Intel
   machines. Enabling this option would allow you to read and write to
-  and from their floppies and harddisk partitions. If you have a
-  floppy or harddisk partition like that, it is probable that they
+  and from their floppies and hard disk partitions. If you have a
+  floppy or hard disk partition like that, it is probable that they
   contain binaries from those other Unix systems; in order to run
   these binaries, you will want to install iBCS2 (iBCS2 [Intel Binary
   Compatibility Standard] is a kernel module which lets you run SCO,
@@ -3729,7 +3746,7 @@
   WordPerfect. It's in tsx-11.mit.edu:/pub/linux/BETA). If you only
   intend to mount files from some other Unix over the network using
   NFS, you don't need the System V filesystem support (but you need
-  nfs filesystem support obviously). Note that this option is
+  NFS filesystem support obviously). Note that this option is
   generally not needed for floppies, since a good portable way to
   transport files and directories between unixes (and even other
   operating systems) is given by the tar program ("man tar").  Note
@@ -3738,9 +3755,8 @@
   Documentation/filesystems/sysv-fs.txt. This option will enlarge your
   kernel by about 34 kB. If you want to compile this as a module ( =
   code which can be inserted in and removed from the running kernel
-  whenever you want), say M here and read
-  Documentation/modules.txt. If you haven't heard about all of this
-  before, it's safe to say N.
+  whenever you want), say M here and read 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
@@ -3761,7 +3777,7 @@
   as well. Enabling this option allows you to mount these partitions
   and diskettes read-only. If you only intend to mount files from
   some other Unix over the network using NFS, you don't need the
-  UFS filesystem support (but you need nfs filesystem support
+  UFS filesystem support (but you need NFS filesystem support
   obviously). Note that this option is generally not needed for
   floppies, since a good portable way to transport files and
   directories between unixes (and even other operating systems)
@@ -3790,21 +3806,21 @@
   incompatible with all others.  Enabling this option allows you to
   read these partition tables and further mount SunOS disks on your
   Linux box if you also have configured BSD ufs filesystem support.
-  This is mainly used to carry data from a Sparc under SunOS to your
+  This is mainly used to carry data from a SPARC under SunOS to your
   Linux box via a removable medium like magneto-optical or ZIP drives.
   If you don't know what all this is about, say N.
 
-SMB filesystem support (to mount WfW shares etc..)
+SMB filesystem support (to mount WfW shares etc...)
 CONFIG_SMB_FS
   SMB (Server Message Buffer) is the protocol Windows for Workgroups
   (WfW), Windows NT and Lan Manager use to talk to each other over an
-  ethernet. Enabling this allows you to mount their filesystems and
+  Ethernet. Enabling this allows you to mount their filesystems and
   access them just like any other unix directory. For details, read
   Documentation/filesystems/smbfs.txt.  Note: if you just want your
   box to act as an SMB *server* and make files and printing services
   available to Windows clients (which need to have a TCP/IP stack),
   you don't need to enable this filesystem support; you can use the
-  program samba (available via ftp (user: anonymous) in
+  program samba (available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/system/Network/samba) for that. General
   information about how to connect Linux, Windows machines and Macs is
   on the WWW at http://eats.com/linux_mac_win.html (to browse the WWW,
@@ -3825,7 +3841,7 @@
 CONFIG_NCP_FS
   NCP (NetWare Core Protocol) is a protocol that runs over IPX and is
   used by Novel NetWare clients to talk to file servers. It is to IPX
-  what nfs is to tcp/ip, if that helps. Enabling this option allows
+  what NFS is to TCP/IP, if that helps. Enabling this option allows
   you to mount NetWare file server volumes and to access them just
   like any other Unix directory. For details, please read the file
   Documentation/filesystems/ncpfs.txt in the kernel source and the
@@ -3837,13 +3853,13 @@
 Amiga FFS filesystem support (EXPERIMENTAL)
 CONFIG_AFFS_FS
   The Fast File System (FFS) is the common filesystem used on
-  harddisks by Amiga (tm) Systems since AmigaOS Version 1.3 (34.20).
+  hard disks by Amiga (tm) Systems since AmigaOS Version 1.3 (34.20).
   It's also possible to mount diskfiles used by
   the Un*X Amiga Emulator by Bernd Schmidt
   (http://www-users.informatik.rwth-aachen.de/~crux/uae.html).
   If you want to do the latter, you will also need the loop device
   support.   Say Y if you want to be able to read and write files from
-  and to an Amiga FFS partition of your harddrive.  Amiga floppies
+  and to an Amiga FFS partition of your hard drive.  Amiga floppies
   however cannot be read with this driver due to an incompatibility of
   the floppy controller used in an Amiga and the standard floppy
   controller in PCs and workstations.  Read
@@ -3857,7 +3873,7 @@
 CONFIG_SERIAL
   This selects whether you want to include the driver for the standard
   serial ports.  People who might say N here are those that are
-  setting up dedicated ethernet WWW/ftp servers, or users that have
+  setting up dedicated Ethernet WWW/FTP servers, or users that have
   one of the various bus mice instead of a serial mouse.  (Note that
   the Cyclades and Stallion multi serial port drivers do not need this
   driver built in for them to work. They are completely independent of
@@ -3874,7 +3890,7 @@
 CONFIG_DIGI
   This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards
   that give you many serial ports. You would need something like this
-  to connect more than two modems to your linux box, for instance in
+  to connect more than two modems to your Linux box, for instance in
   order to become a BBS. If you have a card like that, say Y here and
   read the file Documentation/digiboard.txt.
 
@@ -3882,16 +3898,34 @@
 CONFIG_RISCOM8
   This is a driver for the SDL Communications RISCom/8 multiport card,
   that give you many serial ports. You would need something like this
-  to connect more than two modems to your linux box, for instance in
+  to connect more than two modems to your Linux box, for instance in
   order to become a BBS. If you have a card like that, say Y here and
   read the file Documentation/riscom8.txt. Also it's possible to say
   M here and compile this driver as kernel loadable module.
 
+Specialix IO8+ card support
+CONFIG_SPECIALIX
+  This is a driver for the Specialix IO8+ multiport card (both the
+  ISA and the PCI version), that gives you 8 serial ports. You would 
+  need a card like this to connect more than two modems to your linux 
+  box, for instance in order to become a BBS. If you have a card like 
+  that, say Y here and read the file Documentation/specialix.txt. Also 
+  it's possible to say M here and compile this driver as kernel 
+  loadable module.
+
+Specialix DTR/RTS pin is RTS
+CONFIG_SPECIALIX_RTSCTS
+  The Specialix card can only support either RTS or DTR. When this
+  option is off, the driver will use the pin as "DTR" when the tty is
+  in software handshake mode. When this option is on or hardware 
+  handshake is on, it will always be RTS. Read the file 
+  Documentation/specialix.txt for more information. 
+
 Cyclades async mux support
 CONFIG_CYCLADES
   This is a driver for a card that gives you many serial ports. You
   would need something like this to connect more than two modems to
-  your linux box, for instance in order to become a BBS. If you want
+  your Linux box, for instance in order to become a BBS. If you want
   to compile this as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
   read Documentation/modules.txt. If you haven't heard about it, it's
@@ -3901,7 +3935,7 @@
 Stallion multiport serial support
 CONFIG_STALDRV
   Stallion cards give you many serial ports. You would need something
-  like this to connect more than two modems to your linux box, for
+  like this to connect more than two modems to your Linux box, for
   instance in order to become a BBS. If you say Y here, you will be
   asked for your specific card model in the next questions. Make sure
   to read drivers/char/README.stallion in this case. If you have never
@@ -3929,7 +3963,7 @@
   If you intend to attach a printer to the parallel port of your Linux
   box (as opposed to using a serial printer; if the connector at the
   printer has 9 or 25 holes ["female"], then it's serial), say Y. Also
-  read the Printing-HOWTO, available via ftp (user: anonymous) in
+  read the Printing-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -3943,7 +3977,7 @@
   the printer(s) with the "lp" kernel command line option. (See the
   documentation of your boot loader (lilo or loadlin) about how to
   pass options to the kernel at boot time. The lilo procedure is also
-  explained in the SCSI-HOWTO, available via ftp (user: anonymous) in
+  explained in the SCSI-HOWTO, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.) The standard base addresses
   as well as the syntax of the "lp" command line option can be found
   in drivers/char/lp.c. You can then say Y to the PLIP driver or,
@@ -3958,14 +3992,14 @@
   a serial mouse. Most people have a regular serial MouseSystem or
   Microsoft mouse (made by Logitech) that plugs into a COM port
   (rectangular with 9 or 25 pins). These people say N here. If you
-  have something else, read the Busmouse-HOWTO, available via ftp
+  have something else, read the Busmouse-HOWTO, available via FTP
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO and say Y
   here. If you have a laptop, you either have to check the
   documentation or experiment a bit to find out whether the trackball
   is a serial mouse or not; it's best to say Y here for you. Note that
-  the answer to this question won't directly affect the kernel: saying
-  N will just cause this configure script to skip all the questions
-  about non-serial mice.  If unsure, say Y.
+  the answer to this question won't directly affect the kernel:
+  saying N will just cause this configure script to skip all the
+  questions about non-serial mice.  If unsure, say Y.
 
 Logitech busmouse support
 CONFIG_BUSMOUSE
@@ -3973,7 +4007,7 @@
   generally a round connector with 9 pins. Note that the newer mice
   made by Logitech don't use the Logitech protocol anymore; for those,
   you don't need this option. You want to read the Busmouse-HOWTO,
-  available via ftp (user: anonymous) in
+  available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
@@ -3990,7 +4024,7 @@
   machines. The trackballs of some laptops are PS/2 mice also. In
   particular, the C&T 82C710 mouse on TI Travelmates is a PS/2
   mouse. Although PS/2 mice are not technically bus mice, they are
-  explained in detail in the Busmouse-HOWTO, available via ftp (user:
+  explained in detail in the Busmouse-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to
   compile this as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
@@ -4001,27 +4035,27 @@
 CONFIG_82C710_MOUSE
   This is a certain kind of PS/2 mouse used on the TI Travelmate. If
   you are unsure, try first to say N here and come back if the mouse
-  doesn't work. Read the Busmouse-HOWTO, available via ftp (user:
+  doesn't work. Read the Busmouse-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
 Microsoft busmouse support
 CONFIG_MS_BUSMOUSE
   These animals (also called Inport mice) are connected to an
   expansion board using a round connector with 9 pins. If this is what
-  you have, say Y and read the Busmouse-HOWTO, available via ftp
+  you have, say Y and read the Busmouse-HOWTO, available via FTP
   (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you
   want to compile this as a module ( = code which can be inserted in
   and removed from the running kernel whenever you want), say M here
   and read Documentation/modules.txt. If you are unsure, say N and
   read the HOWTO nevertheless: it will tell you what you have. Also be
   aware that several vendors talk about 'Microsoft busmouse' and
-  actually mean PS/2 busmouse - so count the pins on the connector.
+  actually mean PS/2 busmouse -- so count the pins on the connector.
 
 ATIXL busmouse support
 CONFIG_ATIXL_BUSMOUSE
   This is a rare type of busmouse that is connected to the back of an
   ATI video card. Note that most ATI mice are actually Microsoft
-  busmice. Read the Busmouse-HOWTO, available via ftp (user:
+  busmice. Read the Busmouse-HOWTO, available via FTP (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If you want to
   compile this as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M here and
@@ -4044,7 +4078,7 @@
 CONFIG_QIC02_DYNCONF
   You can either configure this driver once and for all by editing a
   header file, in which case you should say N, or you can fetch a
-  program via anonymous ftp which is able to configure this driver
+  program via anonymous FTP which is able to configure this driver
   during runtime. If you want this, say Y.
 
 Ftape (QIC-80/Travan) support
@@ -4065,8 +4099,8 @@
 
 Zilog serial support
 CONFIG_SUN_ZS
-  This driver does not exist at this point, so you might as well say
-  N.
+  This driver does not exist at this point, so you might as well
+  say N.
 
 Advanced Power Management
 CONFIG_APM
@@ -4127,7 +4161,7 @@
   screen.  Note that this is only used by the VC screen blanker, and
   won't turn off the backlight when using X11 (this also doesn't have
   anything to do with your VESA-compliant power-saving monitor).
-  Further, this option doesn't work for all laptops---it might not
+  Further, this option doesn't work for all laptops -- it might not
   turn off your backlight at all, or it might print a lot of errors to
   the console, especially if you are using gpm.
 
@@ -4224,12 +4258,12 @@
   computer. Every PC has such a clock built in. It can be used to
   generate signals from as low as 1Hz up to 8192Hz, and can also be
   used as a 24 hour alarm.  It reports status information via the file
-  /proc/rtc and its behaviour is set by various ioctls on
-  /dev/rtc. People running SMP (= multiprocessor) versions of Linux
-  should enable this option to read and set the RTC clock in a SMP
-  compatible fashion. If you think you have a use for such a device
-  (such as periodic data sampling), then say Y here, and go read the
-  file Documentation/rtc.txt for details.
+  /proc/rtc and its behaviour is set by various ioctls on /dev/rtc.
+  People running SMP ( = multiprocessor) versions of Linux should
+  enable this option to read and set the RTC clock in a SMP compatible
+  fashion. If you think you have a use for such a device (such as
+  periodic data sampling), then say Y here, and go read the file
+  Documentation/rtc.txt for details.
 
 Sound card support
 CONFIG_SOUND
@@ -4237,7 +4271,7 @@
   than an occasional beep, say Y. Be sure to have all the information
   about your sound card and its configuration down (I/O port,
   interrupt and DMA channel), because you will be asked for it. You
-  want to read the Sound-HOWTO, available via ftp (user: anonymous)
+  want to read the Sound-HOWTO, available via FTP (user: anonymous)
   from sunsite.unc.edu:/pub/Linux/docs/HOWTO. There is also some
   information in various README files in drivers/sound.  If you want
   to compile this as a module ( = code which can be inserted in and
@@ -4282,7 +4316,7 @@
 MPU-401 support (NOT for SB16)
 CONFIG_MPU401
   Be careful with this question. The MPU401 interface is supported by
-  all soundcards. However, some natively supported cards have their
+  all sound cards. However, some natively supported cards have their
   own driver for MPU401. Enabling the MPU401 option with these cards
   will cause a conflict. Also, enabling MPU401 on a system that
   doesn't really have a MPU401 could cause some trouble. If your card
@@ -4338,7 +4372,7 @@
 
 Ensoniq Soundscape support
 CONFIG_SSCAPE
-  Answer Y if you have a soundcard based on the Ensoniq SoundScape
+  Answer Y if you have a sound card based on the Ensoniq SoundScape
   chipset. Such cards are being manufactured at least by Ensoniq, Spea
   and Reveal (Reveal makes also other cards).
 
@@ -4383,7 +4417,7 @@
 
 Sun Audio support
 CONFIG_SUN_AUDIO
-  This is support for the soundcards on Sun workstations. The code
+  This is support for the sound cards on Sun workstations. The code
   does not exist yet, so you might as well say N here.
 
 Kernel profiling support
@@ -4394,7 +4428,7 @@
   it, you need the readprofile package from sunsite.unc.edu. Its
   manpage gives information regarding the format of profiling data. To
   become a kernel hacker, you can start with the Kernel Hacker's
-  Guide, available via ftp (user: anonymous) from
+  Guide, available via FTP (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/LDP. Mere mortals say N.
 
 Profile shift count
@@ -4436,8 +4470,8 @@
 Support generic MP (RFC 1717)
 CONFIG_ISDN_MPP
   With synchronous PPP enabled, it is possible to increase throughput
-  by bundling several ISDN-connections, using this protocol. See
-  Documentation/isdn/README.syncppp for more information.
+  by bundling several ISDN-connections, using this protocol.
+  See Documentation/isdn/README.syncppp for more information.
 
 Use VJ-compression with synchronous PPP
 CONFIG_ISDN_PPP_VJ
@@ -4470,92 +4504,152 @@
   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.
+  many compatibles). It supports other chipsets too.
   See Documentation/isdn/README.HiSax for further informations on
   using this driver.
 
+HiSax Support for EURO/DSS1
+CONFIG_HISAX_EURO
+  Enable this if you have a EURO ISDN line.
+
+Support for german chargeinfo
+CONFIG_DE_AOC
+  If you have german AOC, you can enable this to get the charginfo.
+
+Disable sending complete
+CONFIG_HISAX_NO_SENDCOMPLETE
+  If you have trouble with some ugly exchanges or you live in
+  Australia select this option.
+
+Disable sending low layer compatibility
+CONFIG_HISAX_NO_LLC
+  If you have trouble with some ugly exchanges try to select this
+  option.
+
+HiSax Support for german 1TR6
+CONFIG_HISAX_1TR6
+  Enable this if you have a old german 1TR6 line.
+  Note: Many older local switches are using 1TR6 on internal S0.
+
 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.
+  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.
+  See Documentation/isdn/README.HiSax on how to configure it using the
+  different cards, a different D-channel protocol, or non-standard
+  IRQ/port settings.
+
+HiSax Support for Teles 16.3c
+CONFIG_HISAX_TELES3C
+  This enables HiSax support for the Teles 16.3c PnP.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for Teles PCI
+CONFIG_HISAX_TELESPCI
+  This enables HiSax support for the Teles PCI.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for Teles S0Box
+CONFIG_HISAX_S0BOX
+  This enables HiSax support for the Teles/Creatix parallel port
+  S0BOX.  See Documentation/isdn/README.HiSax on how to configure it.
 
 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.
+  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 settings.
+
+HiSax Support for AVM PnP/PCI (Fritz!PNP/PCI)
+CONFIG_HISAX_FRITZPCI
+  This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for AVM A1 PCMCIA (Fritz)
+CONFIG_HISAX_AVM_A1_PCMCIA
+  This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
+  See Documentation/isdn/README.HiSax on how to configure it.
 
 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.
+CONFIG_HISAX_ELSA
+  This enables HiSax support for all Elsa cards.
+  See Documentation/isdn/README.HiSax on how to configure it using the
+  different cards, a different D-channel protocol, or non-standard
+  IRQ/port 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.
+  See Documentation/isdn/README.HiSax on how to configure it.
 
-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.
+HiSax Support for Eicon.Diehl Diva cards
+CONFIG_HISAX_DIEHLDIVA
+  This enables HiSax support for all none Pro versions of
+  Eicon.Diehl's Diva series passiv cards.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for ASUSCOM cards
+CONFIG_HISAX_ASUSCOM
+  This enables HiSax support for all passiv AsusCom and AsusComs OEM cards,
+  e.g PCBIT-DP, Dynalink and much more.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for TELEINT cards
+CONFIG_HISAX_TELEINT
+  This enables HiSax support for TeleInts semi-activ card and for
+  other HFC-2BS0 based cards.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for Sedlbauer cards
+CONFIG_HISAX_SEDLBAUER
+  This enables HiSax support for all Sedlbauer passiv cards such as
+  Sedlbauer Speed Card (Speed Win, Teledat 100), Speed Star
+  and Speed Star2 (PCMCIA), ISDN-Controller PC/104, Speed PCI and
+  Speed Fax+.
+  
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for USR Sportster internal TA
+CONFIG_HISAX_SPORTSTER
+  This enables HiSax support for the USR (3Com) Sportster internal TA
+  passiv card.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for MIC card
+CONFIG_HISAX_MIC
+  This enables HiSax support for the MIC passiv card.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for NETjet card
+CONFIG_HISAX_NETJET
+  This enables HiSax support for the NetJet PCI and maybe for other
+  Tiger300 based passiv cards.
+  See Documentation/isdn/README.HiSax on how to configure it.
+
+HiSax Support for Niccy PnP/PCI card
+CONFIG_HISAX_NICCY
+  This enables HiSax support for Dr. Neuhaus (Sagem) Niccy series
+  passiv cards.
+  See Documentation/isdn/README.HiSax on how to configure it.
 
 PCBIT-D support
 CONFIG_ISDN_DRV_PCBIT
   This enables support for the PCBIT ISDN-cards. This card is
   manufactured in Portugal by Octal. 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 and Documentation/isdn/README.pcbit for
-  more information.
+  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
@@ -4574,25 +4668,32 @@
 
 Support for AP1000 multicomputer
 CONFIG_AP1000
-  This enables support for a sparc based parallel multi-computer
+  This enables support for a SPARC based parallel multi-computer
   called an AP1000+. For details on our efforts to port Linux to this
   machine see http://cap.anu.edu.au/cap/projects/linux or mail to
   hackers@cafe.anu.edu.au
 
-Sparc ESP SCSI support
+SPARC ESP SCSI support
 CONFIG_SCSI_SUNESP
   This is the driver for the Sun ESP SCSI host adapter. The ESP
   chipset is present in most SPARC-based computers.
 
-Sparc /dev/openprom compatibility driver
+SPARC /dev/openprom compatibility driver
 CONFIG_SUN_OPENPROMIO
-  This driver provides user programs with an interface to the Sparc
+  This driver provides user programs with an interface to the SPARC
   PROM device tree. The driver implements a SunOS-compatible
   interface and a NetBSD-compatible interface. If you want to
   compile this as a module ( = code which can be inserted in and
   removed from the running kernel whenever you want), say M and read
   Documentation/modules.txt. If unsure, say Y.
 
+Handle buggy SMP BIOSes with bad MTRR setup
+CONFIG_MTRR
+  Some BIOSes for MP1.1/MP1.4 SMP machines fail to set the MTRR
+  registers in the chips up correctly as the specification and Intel
+  rules require. If you have a PPro or later SMP and one or more CPU's
+  report a value of about 2-3 bogomips enable this.
+
 # need an empty line after last entry, for sed script in Configure.
 
 #
@@ -4600,16 +4701,16 @@
 #
 # LocalWords:  CONFIG coprocessor DX Pentium SX lilo loadlin HOWTO ftp sunsite
 # LocalWords:  unc edu docs emu README kB BLK DEV FD Thinkpad fd MFM RLL IDE gz
-# LocalWords:  cdrom harddisk diskless netboot nfs xzvf ATAPI MB harddrives ide
-# LocalWords:  HD harddisks CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios
+# LocalWords:  cdrom diskless netboot nfs xzvf ATAPI MB ide
+# LocalWords:  HD CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios
 # LocalWords:  ISA EISA Microchannel VESA BIOSes IPC SYSVIPC ipc Ctrl dmesg hlt
-# LocalWords:  BINFMT Linkable http ac uk jo html GCC Sparc AVANTI CABRIOLET EB
+# LocalWords:  BINFMT Linkable http ac uk jo html GCC SPARC AVANTI CABRIOLET EB
 # LocalWords:  netscape gcc LD CC toplevel MODVERSIONS insmod rmmod modprobe IP
-# LocalWords:  genksyms INET loopback gatewaying ethernet internet PPP ARP Arp
+# LocalWords:  genksyms INET loopback gatewaying Ethernet internet PPP ARP Arp
 # LocalWords:  howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip
 # LocalWords:  proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS
 # LocalWords:  telneting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl
-# LocalWords:  Mb SKB IPX Novell Netware dosemu Appletalk DDP ATALK tapedrive
+# LocalWords:  Mb SKB IPX Novell NetWare dosemu AppleTalk DDP ATALK tapedrive
 # LocalWords:  SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA
 # LocalWords:  buslogic DMA DPT ATT eata dma PIO UltraStor fdomain umsdos ext
 # LocalWords:  QLOGIC qlogic TMC seagate Trantor ultrastor FASST wd NETDEVICES
@@ -4622,26 +4723,26 @@
 # LocalWords:  TR Sony CDU caddyless cdu Mitsumi MCD cd mcd XA MultiSession CDA
 # LocalWords:  Matsushita Panasonic SBPCD Soundblaster Longshine sbpcd Aztech
 # LocalWords:  Okano Wearnes AZTCD CDD SE aztcd sonycd Goldstar GSCD Philips fs
-# LocalWords:  LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos harddrive mtools
+# LocalWords:  LMS OPTCD Sanyo SJCD minix faqs xiafs XIA msdos mtools
 # LocalWords:  std softlinks umssync NetworkFileSharing nfsd mountd CDs HPFS TI
-# LocalWords:  hpfs SYSV SCO intel iBCS Wyse WordPerfect tsx mit unixes sysv NR
+# LocalWords:  hpfs SYSV SCO Intel iBCS Wyse WordPerfect tsx mit unixes sysv NR
 # LocalWords:  SMB WfW Cyclades async mux Logitech busmouse MouseSystem aka AST
 # LocalWords:  PSMOUSE Compaq trackballs Travelmate Inport ATIXL ATI busmice ld
 # LocalWords:  gpm config QIC DYNCONF FTAPE Stor Ftape ftape pcsndrv manpage NT
 # LocalWords:  readprofile diskdrives org com masq EtherTalk tcp netrom sunacm
 # LocalWords:  misc AIC aic pio nullmodems scc Portmaster eql GIS PhotoCDs MCDX
-# LocalWords:  mcdx gscd optcd sjcd ISP soundcard hdparm Workgroups Lan samba
+# LocalWords:  mcdx gscd optcd sjcd ISP hdparm Workgroups Lan samba
 # LocalWords:  filesystems smbfs ATA ppp PCTech RZ www powerquest txt CMD ESDI
-# LocalWords:  chipset FB multicast MROUTE appletalk ifconfig IBMTR multiport
+# LocalWords:  chipset FB multicast MROUTE ifconfig IBMTR multiport
 # LocalWords:  Multisession STALDRV EasyIO EC EasyConnection ISTALLION ONboard
 # LocalWords:  Brumby pci TNC cis ohio faq usenet NETLINK dev hydra ca Tyne mem
 # LocalWords:  carleton Deskstation DECstation SUNFD JENSEN Noname XXXM SLiRP
-# LocalWords:  pppd Zilog ZS soundcards SRM bootloader ez mainmenu rarp ipfwadm
+# LocalWords:  pppd Zilog ZS SRM bootloader ez mainmenu rarp ipfwadm
 # LocalWords:  RTNETLINK mknod xos MTU lwared Macs mac netatalk macs cs Wolff
 # LocalWords:  dartmouth flowerpt MultiMaster FlashPoint tudelft etherexpress
 # LocalWords:  ICL EtherTeam ETH IDESCSI TXC SmartRAID SmartCache httpd sjc dlp
 # LocalWords:  thesphere TwoServers BOOTP DHCP ncpfs BPQETHER BPQ chipsets MG
-# LocalWords:  bsd comp Sparcstation le SunOS ie Gracilis PackeTwin PT pt LU FX
+# LocalWords:  bsd comp SPARCstation le SunOS ie Gracilis PackeTwin PT pt LU FX
 # LocalWords:  FX TEAC SoundBlaster CR CreativeLabs LCS mS ramdisk IDETAPE cmd
 # LocalWords:  Vertos Genoa Funai hsfs NCP NetWare tgz APM apm ioctls UltraLite
 # LocalWords:  TravelMate CDT LCD backlight VC RPC Mips DECStation AXP barlow
diff -u --recursive --new-file v2.0.35/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd
--- v2.0.35/linux/Documentation/cdrom/aztcd	Mon Aug  5 00:13:50 1996
+++ linux/Documentation/cdrom/aztcd	Sun Nov 15 10:32:42 1998
@@ -43,12 +43,12 @@
 1. NOTE 
 This software has been successfully in alpha and beta test and is part of
 the standard kernel since kernel 1.1.8x since December 1994. It works with
-with AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and
-CONRAD TXC (Nr.99 31 23 -series 04) and has proven to be stable with kernel 
-versions 1.0.9 to 1.3.72. But with any software there still may be bugs in it. 
-So if you encounter problems, you are invited to help us improve this software. 
-Please send me a detailed bug report (see chapter BUG REPORTS). You are also 
-invited in helping us to increase the number of drives, which are supported.
+AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and CONRAD TXC
+(Nr.99 31 23 -series 04) and has proven to be stable with kernel versions 1.0.9
+to 1.3.72. But with any software there still may be bugs in it. So if you
+encounter problems, you are invited to help us improve this software. Please
+send me a detailed bug report (see chapter BUG REPORTS). You are also invited
+in helping us to increase the number of drives, which are supported.
 
 Please read the README-files carefully and always keep a backup copy of your 
 old kernel, in order to reboot if something goes wrong!
diff -u --recursive --new-file v2.0.35/linux/Documentation/filesystems/vfat.txt linux/Documentation/filesystems/vfat.txt
--- v2.0.35/linux/Documentation/filesystems/vfat.txt	Mon Jul 13 13:46:24 1998
+++ linux/Documentation/filesystems/vfat.txt	Sun Nov 15 10:32:42 1998
@@ -18,7 +18,7 @@
 		 know how to deal with Unicode. There is also an option of
 		 doing UTF8 translations with the utf8 option.
 utf8	      -- UTF8 is the filesystem safe version of Unicode that
-		 is used by the console.  It can be be enabled for the
+		 is used by the console.  It can be enabled for the
 		 filesystem with this option. If 'uni_xlate' gets set,
 		 UTF8 gets disabled.
 uni_xlate     -- Translate unhandled Unicode characters to special
diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/HiSax.cert linux/Documentation/isdn/HiSax.cert
--- v2.0.35/linux/Documentation/isdn/HiSax.cert	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/HiSax.cert	Sun Nov 15 10:32:42 1998
@@ -0,0 +1,76 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+First:
+
+    HiSax 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.
+
+However, if you wish to modify the HiSax sources, please note the following:
+
+HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards.
+The certification is only valid for the combination of the tested software
+version and the tested hardware. Any changes to the HiSax source code may
+therefore affect the certification.
+
+If you change the main files of the HiSax ISDN stack, the certification will
+become invalid. Because in most countries it is illegal to connect
+unapproved ISDN equipment to the public network, I have to guarantee that
+changes in HiSax do not affect the certification.
+
+In order to make a valid certification apparent to the user, I have built in
+some validation checks that are made during the make process. The HiSax main
+files are protected by md5 checksums and the md5sum file is pgp signed by
+myself:
+
+KeyID 1024/FF992F6D 1997/01/16 Karsten Keil <keil@temic-ech.spacenet.de>
+Key fingerprint = 92 6B F7 58 EE 86 28 C8  C4 1A E6 DC 39 89 F2 AA
+
+Only if the checksums are OK, and the signature of the file
+"drivers/isdn/hisax/md5sums.asc" match, is the certification valid; a
+message confirming this is then displayed during the hisax init process.
+
+The affected files are:
+
+drivers/isdn/hisax/isac.c
+drivers/isdn/hisax/isdnl1.c
+drivers/isdn/hisax/isdnl2.c
+drivers/isdn/hisax/isdnl3.c
+drivers/isdn/hisax/tei.c
+drivers/isdn/hisax/callc.c
+drivers/isdn/hisax/l3dss1.c
+drivers/isdn/hisax/l3_1tr6.c
+drivers/isdn/hisax/cert.c
+drivers/isdn/hisax/elsa.c
+
+Please send any changes, bugfixes and patches to me rather than implementing
+them directly into the HiSax sources.
+
+This does not reduce your rights granted by the GNU General Public License.
+If you wish to change the sources, go ahead; but note that then the
+certification is invalid even if you use ELSA Quickstep cards.
+
+Here are the certification registration numbers for ELSA Quickstep cards:
+German   D133361J CETECOM ICT Services GmbH 0682
+European D133362J CETECOM ICT Services GmbH 0682
+
+
+Karsten Keil
+keil@isdn4linux.de
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBNj5OKDpxHvX/mS9tAQFHuQP/WeImlqCcDZ2d132yAvRBWFULlJoSf1P/
+c1lVTeaWvsSaY5Cu9hrKhXXhPzeEaitUbcUBPXdpzFWCA5CE902lnz7AhgRC+HF1
+0qiKgkZZyc/5HKasFymR1+IWSLw30GesP3Di/ZMR3NJi8SlY9PIjx7hnEMunGSRO
+1ufPvfWWuu8=
+=nGJk
+-----END PGP SIGNATURE-----
diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.0.35/linux/Documentation/isdn/README	Sun Nov 15 10:49:23 1998
+++ linux/Documentation/isdn/README	Sun Nov 15 10:32:42 1998
@@ -41,11 +41,11 @@
   In the following Text, the terms MSN and EAZ are used.
 
   MSN is the abbreviation for (M)ultiple(S)ubscriber(N)umber, and applies
-  to Euro(EDSS1)-type lines. Usually it is simply the phone-number.
+  to Euro(EDSS1)-type lines. Usually it is simply the phone number.
 
   EAZ is the abbreviation of (E)ndgeraete(A)uswahl(Z)iffer and
   applies to German 1TR6-type lines. This is a one-digit string,
-  simply appended to the base phone-number
+  simply appended to the base phone number
 
   The internal handling is nearly identical, so replace the appropriate
   term to that one, which applies to your local ISDN-environment.
@@ -62,7 +62,7 @@
      read:  raw D-channel-messages (format: depends on driver).
      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 
+            also the boot-code and the protocol software can be loaded into 
             the card.
 
    O N L Y !!!  for debugging (no locking against other devices):
@@ -74,7 +74,7 @@
 
    128 tty-devices (64 cuix and 64 ttyIx) with integrated modem-emulator:
    The functionality is almost the same as that of a serial device
-   (the line-discs are handled by the kernel, which lets you run 
+   (the line-discs are handled by the kernel), which lets you run 
    SLIP, CSLIP and asynchronous PPP through the devices. We have tested 
    Seyon, minicom, CSLIP (uri-dip) PPP and mgetty (compiled with NO_FAX),
    XCept.
@@ -82,57 +82,57 @@
    The modem-emulation supports the following:
            1.3.1 Commands:
 
-               ATA	Answer incoming call.
-	       ATD<No.> Dial, the number may contain:
+               ATA      Answer incoming call.
+               ATD<No.> Dial, the number may contain:
                         [0-9] and [,#.*WPT-S]
                         the latter are ignored until 'S'.
                         The 'S' must precede the number, if 
                         the line is a SPV (German 1TR6).
-               ATE0	Echo off.
-	       ATE1     Echo on (default).
+               ATE0     Echo off.
+               ATE1     Echo on (default).
                ATH      Hang-up.
-	       ATH1     Off hook (ignored).
+               ATH1     Off hook (ignored).
                ATH0     Hang-up.
-	       ATI      Return "ISDN for Linux...".
+               ATI      Return "ISDN for Linux...".
                ATI0        "
                ATI1        "
-			   ATI2 Report of last connection.
+               ATI2     Report of last connection.
                ATO      On line (data mode).
                ATQ0     Enable result codes (default).
                ATQ1     Disable result codes (default).
-               ATSx=y	Set register x to y.
-	       ATSx?    Show contents of register x.
+               ATSx=y   Set register x to y.
+               ATSx?    Show contents of register x.
                ATV0     Numeric responses.
                ATV1     English responses (default).
-	       ATZ      Load registers and EAZ/MSN from Profile.
-	       AT&Bx    Set Send-Packet-size to x (max. 4000)
+               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 HiSax-Module-
+                        low-level-driver used. e.g. the HiSax-Module-
                         limit is 2000. You will get NO Error-Message,
-                        if you set it to higher Values, because at the
+                        if you set it to higher values, because at the
                         time of giving this command the corresponding
                         driver may not be selected (see "Automatic
                         Assignment") however the size of outgoing packets
                         will be limited correctly.
-	       AT&D0    Ignore DTR
-	       AT&D2    DTR-low-edge: Hang up and return to 
+               AT&D0    Ignore DTR
+               AT&D2    DTR-low-edge: Hang up and return to 
                         command mode (default).
                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 (x = 1..8) (not yet implemented)
-	       AT&V     Show all settings.
+               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 (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).
-               AT&X0	BTX-mode off (default)
-	       AT&X1    BTX-mode on. (S13.1=1, S14=0, S16=7, S18=7, S19=0)
+               AT&X0    BTX-mode off (default)
+               AT&X1    BTX-mode on. (S13.1=1, S14=0, S16=7, S18=7, S19=0)
 
            For voice-mode commands refer to README.audio
 
-	   1.3.2 Escape sequence:
+           1.3.2 Escape sequence:
                During a connection, the emulation reacts just like
                a normal modem to the escape sequence <DELAY>+++<DELAY>.
-	       (The escape character - default '+' - can be set in the
+               (The escape character - default '+' - can be set in the
                register 2).
                The DELAY must at least be 1.5 seconds long and delay 
                between the escape characters must not exceed 0.5 seconds.
@@ -180,19 +180,19 @@
                                       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 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
+             14   0         Layer-2 protocol:
+                                      0 = X75/LAPB with I-frames
+                                      1 = X75/LAPB with UI-frames
                                       2 = X75/LAPB with BUI-frames
                                       3 = HDLC
                                       4 = Transparent (audio)
              15   0         Layer-3 protocol: (at the moment always 0)
                                       0 = transparent
-	     16   250       Send-Packet-size/16
+             16   250       Send-Packet-size/16
              17   8         Window-size (not yet implemented)
              18   4         Bit coded register, Service-Octet-1 to accept,
                             or to be used on dialout:
@@ -211,7 +211,7 @@
              19   0         Service-Octet-2
              20   0         Bit coded register (readonly)
                             Service-Octet-1 of last call.
-                            Bit mapping is the same like register 18
+                            Bit mapping is the same as register 18
              21   0         Bit coded register (readonly)
                             Set on incoming call (during RING) to
                             octet 3 of calling party number IE (Numbering plan)
@@ -229,17 +229,17 @@
   All inactive physical lines are listening to all EAZs for incoming
   calls and are NOT assigned to a specific tty or network interface.
   When an incoming call is detected, the driver looks first for a network 
-  interfaces and then for an opened tty which:
+  interface and then for an opened tty which:
 
   1. is configured for the same EAZ.
   2. has the same protocol settings for the B-channel.
   3. (only for network interfaces if the security flag is set)
      contains the caller number in its access list.
   4. Either the channel is not bound exclusively to another Net-interface, or
-     it is bound AND the other checks apply to exact this Interface.
+     it is bound AND the other checks apply to exactly this interface.
      (For usage of the bind-features, refer to the isdnctrl-man-page)
 
-  Only when a matching interface or tty is found, the call is accepted
+  Only when a matching interface or tty is found is the call accepted
   and the "connection" between the low-level-layer and the link-level-layer
   is established and kept until the end of the connection.
   In all other cases no connection is established. Isdn4linux can be
@@ -275,8 +275,8 @@
 
 4. Device-inodes
 
-   The major and minor-numbers and its names are described in
-   Documentation/devices.txt. The major-numbers are:
+   The major and minor numbers and their names are described in
+   Documentation/devices.txt. The major numbers are:
 
      43 for the ISDN-tty's.
      44 for the ISDN-callout-tty's.
@@ -285,7 +285,7 @@
 5. Application
 
    a) For some card-types, firmware has to be loaded into the cards, before
-      proceeding with device-independant setup. See README.<yourDriver>
+      proceeding with device-independent setup. See README.<yourDriver>
       for how to do that.
 
    b) If you only intend to use ttys, you are nearly ready now.
@@ -321,23 +321,42 @@
       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.
+   i) Set the dial mode of the interface:
+       isdnctrl dialmode isdn0 auto
+      "off" means that you (or the system) cannot make any connection
+        (neither incoming or outgoing connections are possible). Use
+        this if you want to be sure that no connections will be made.
+      "auto" means that the interface is in auto-dial mode, and will
+        attempt to make a connection whenever a network data packet needs
+        the interface's link. Note that this can cause unexpected dialouts,
+        and lead to a high phone bill! Some daemons or other pc's that use
+        this interface can cause this.
+        Incoming connections are also possible.
+      "manual" is a dial mode created to prevent the unexpected dialouts.
+        In this mode, the interface will never make any connections on its
+        own. You must explicitly initiate a connection with "isdnctrl dial
+        isdn0". You _must_ also hangup the line explicitly as well! There
+        is NO timeout in this mode. Use "isdnctrl hangup isdn0" to end the
+        connection.
+        "manual" is the default.
 
-   j) (optional) If you run X11 and have Tcl/Tk-wish Version4.0, you can use
+   j) Setup the interface with ifconfig as usual, and set a route to it.
+
+   k) (optional) If you run X11 and have Tcl/Tk-wish version 4.0, you can use
      the script tools/tcltk/isdnmon. You can add actions for line-status
      changes. See the comments at the beginning of the script for how to
      do that. There are other tty-based tools in the tools-subdirectory
      contributed by Michael Knigge (imon), Volker Götz (imontty) and
      Andreas Kool (isdnmon).
 
-   k) For initial testing, you can set the verbose-level to 2 (default: 0).
+   l) For initial testing, you can set the verbose-level to 2 (default: 0).
       Then all incoming calls are logged, even if they are not addressed
       to one of the configured net-interfaces:
       isdnctrl verbose 2
 
-  Now you are ready! A ping to the set address should now result in a
-  dial-out (look at syslog kernel-messages).
-  The phone-numbers and EAZs can be assigned at any time with isdnctrl.
+  Now you are ready! A ping to the set address should now result in an
+  automatic dial-out (look at syslog kernel-messages).
+  The phone numbers and EAZs can be assigned at any time with isdnctrl.
   You can add as many interfaces as you like with addif following the
   directions above. Of course, there may be some limitations. But we have 
   tested as many as 20 interfaces without any problem. However, if you 
@@ -365,7 +384,7 @@
 
 
    "isdnctrl secure <InterfaceName> off" 
-   Switch of secure operation (default).
+   Switch off secure operation (default).
 
    "isdnctrl ihup <InterfaceName> [on|off]" 
    Switch the hang-up-timer for incoming calls on or off.
@@ -400,7 +419,7 @@
    Selects the type of packet-encapsulation. The encapsulation can be changed
    only while an interface is down.
 
-   At the moment th following Values are supported:
+   At the moment the following values are supported:
  
    rawip    (Default) Selects raw-IP-encapsulation. This means, MAC-headers
             are stripped off.  
@@ -436,13 +455,13 @@
    Forces hangup of an interface.
 
    "isdnctrl bind <InterfaceName> <DriverId>,<ChannelNumber> [exclusive]"
-   If you are using more than one ISDN-Card, it is sometimes necessary to
-   dial out using a specific Card or even preserve a specific Channel for
-   Dialout of a specific net-interface. This can be done with the above
+   If you are using more than one ISDN card, it is sometimes necessary to
+   dial out using a specific card or even preserve a specific channel for
+   dialout of a specific net-interface. This can be done with the above
    command. Replace <DriverId> by whatever you assigned while loading the
-   module. The <ChannelNumber> is counting from zero. the upper Limit
-   depends on the card used. At the Moment no card supports more than
-   2 Channels, so the upper limit is one.
+   module. The <ChannelNumber> is counted from zero. The upper limit
+   depends on the card used. At the moment no card supports more than
+   2 channels, so the upper limit is one.
 
    "isdnctrl unbind <InterfaceName>"
    unbinds a previously bound interface.
@@ -451,8 +470,8 @@
    If switched on, isdn4linux replies a REJECT to incoming calls, it
    cannot match to any configured interface.
    If switched off, nothing happens in this case.
-   You normally should NOT enable this feature, if the ISDN-adaptor is not
-   the only device, connected to the S0-bus. Otherwise it could happen, that
+   You normally should NOT enable this feature, if the ISDN adapter is not
+   the only device connected to the S0-bus. Otherwise it could happen that
    isdn4linux rejects an incoming call, which belongs to another device on
    the bus. 
 
@@ -507,7 +526,7 @@
    If other drivers will not be affected, I will include the changes
    in the next release.
    For developers only, there is a second mailing-list. Write to me
-   (fritz@wuemaus.franken.de), if you want to join that list.
+   (fritz@isdn4linux.de), if you want to join that list.
 
 Have fun!
 
diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax
--- v2.0.35/linux/Documentation/isdn/README.HiSax	Mon Aug  4 17:33:59 1997
+++ linux/Documentation/isdn/README.HiSax	Sun Nov 15 10:32:43 1998
@@ -1,7 +1,7 @@
 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 
+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
@@ -23,24 +23,50 @@
 ---------------
 
 Teles 8.0/16.0/16.3 and compatible ones
+Teles 16.3c
 Teles S0/PCMCIA
-Creatix PnP S0 
-AVM A1 (Fritz)
+Teles PCI
+Teles S0Box
+Creatix S0Box
+Creatix PnP S0
+Compaq ISDN S0 ISA card
+AVM A1 (Fritz, Teledat 150)
+AVM Fritz PCMCIA
+AVM Fritz PnP
+AVM Fritz PCI
 ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8
 ELSA Quickstep 1000
+ELSA Quickstep 1000PCI
+ELSA Quickstep 3000 (same settings as QS1000)
+ELSA Quickstep 3000PCI
 ELSA PCMCIA
 ITK ix1-micro Rev.2
+Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
+Eicon.Diehl Diva Piccola
+ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D)
+Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter)
+PCBIT-DP (OEM version of ASUSCOM NETWORK INC. ISDNLink)
+HFC-2BS0 based cards (TeleInt SA1)
+Sedlbauer Speed Card (Speed Win, Teledat 100, PCI, Fax+)
+Sedlbauer Speed Star/Speed Star2 (PCMCIA)
+Sedlbauer ISDN-Controller PC/104
+USR Sportster internal TA (compatible Stollmann tina-pp V3)
+ith Kommunikationstechnik GmbH MIC 16 ISA card
+Traverse Technologie NETjet PCI S0 card
+Dr. Neuhaus Niccy PnP/PCI
 
 Note: PCF, PCF-Pro: up to now, only the ISDN part is supported
       PCC-8: not tested yet
       Teles PCMCIA is EXPERIMENTAL
+      Teles 16.3c is EXPERIMENTAL
+      Teles PCI is EXPERIMENTAL
+      Teles S0Box is EXPERIMENTAL
+      Eicon.Diehl Diva U interface not tested
 
 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.
+(io, mem, irq).
 
 
 Configuring the driver
@@ -51,7 +77,7 @@
 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
+modules. It is included 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
@@ -98,7 +124,7 @@
 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 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:
@@ -113,15 +139,36 @@
     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)
+    7   ELSA Quickstep 1000      irq, io  (from isapnp setup)
     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.
+   10   ELSA PCMCIA		 irq, io  (set with card manager)
+   11   Eicon.Diehl Diva ISA PnP irq, io
+   11   Eicon.Diehl Diva PCI     no parameter
+   12   ASUS COM ISDNLink        irq, io  (from isapnp setup)
+   13   HFC-2BS0 based cards     irq, io
+   14   Teles 16.3c PnP          irq, io
+   15   Sedlbauer Speed Card     irq, io
+   15   Sedlbauer PC/104         irq, io
+   15   Sedlbauer Speed PCI	 no parameter
+   16   USR Sportster internal   irq, io
+   17   MIC card                 irq, io
+   18   ELSA Quickstep 1000PCI   no parameter
+   19   Compaq ISDN S0 ISA card  irq, io0, io1, io (from isapnp setup io=IO2)
+   20   NETjet PCI card          no parameter
+   21   Teles PCI                no parameter
+   22   Sedlbauer Speed Star (PCMCIA) irq, io (set with card manager)
+   24   Dr. Neuhaus Niccy PnP    irq, io0, io1 (from isapnp setup)
+   24   Dr. Neuhaus Niccy PCI    no parameter
+   25   Teles S0Box              irq, io (of the used lpt port)
+   26   AVM A1 PCMCIA (Fritz!)   irq, io (set with card manager)
+   27   AVM PnP (Fritz!PnP)      irq, io  (from isapnp setup)
+   27   AVM PCI (Fritz!PCI)      no parameter
+   28   Sedlbauer Speed Fax+     irq, io (from isapnp setup)
+
+
+At the moment IRQ sharing is only possible with PCI cards. Please make sure
+that your IRQ is free and enabled for ISA use.
 
 
 Examples for module loading
@@ -138,7 +185,7 @@
 4. Any ELSA PCC/PCF card, Euro ISDN
    modprobe hisax type=6 protocol=2
 
-5. Teles 16.3 PnP, Euro ISDN, with isapnp configured 
+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))
@@ -179,28 +226,50 @@
                 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
+
+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
+    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
+    7   ELSA Quickstep 1000     ONLY WORKS AS A MODULE !
+    8   Teles S0 PCMCIA         pa=irq  pb=iobase
     9   ITK ix1-micro Rev.2     pa=irq  pb=iobase
+   10   ELSA PCMCIA             pa=irq, pb=io  (set with card manager)
+   11   Eicon.Diehl Diva ISAPnP ONLY WORKS AS A MODULE !
+   11   Eicon.Diehl Diva PCI    no parameter
+   12   ASUS COM ISDNLink       ONLY WORKS AS A MODULE !
+   13	HFC-2BS0 based cards    pa=irq  pb=io
+   14   Teles 16.3c PnP         ONLY WORKS AS A MODULE !
+   15	Sedlbauer Speed Card    pa=irq  pb=io (Speed Win only as module !)
+   15   Sedlbauer PC/104        pa=irq  pb=io
+   15   Sedlbauer Speed PCI	no parameter
+   16   USR Sportster internal  pa=irq  pb=io
+   17   MIC card                pa=irq  pb=io
+   18   ELSA Quickstep 1000PCI  no parameter
+   19   Compaq ISDN S0 ISA card ONLY WORKS AS A MODULE !
+   20   NETjet PCI card         no parameter
+   21   Teles PCI               no parameter
+   22   Sedlbauer Speed Star (PCMCIA)  pa=irq, pb=io  (set with card manager)
+   24   Dr. Neuhaus Niccy PnP   ONLY WORKS AS A MODULE !
+   24   Dr. Neuhaus Niccy PCI   no parameter
+   25   Teles S0Box             irq, io (of the used lpt port)
+   26   AVM A1 PCMCIA (Fritz!)  irq, io (set with card manager)
+   27   AVM PnP (Fritz!PnP)     ONLY WORKS AS A MODULE !
+   27   AVM PCI (Fritz!PCI)     no parameter
+   28   Sedlbauer Speed Fax+    ONLY WORKS AS A MODULE !
 
 
 Running the driver
 ------------------
 
-When you insmod isdn.o and hisax.o (or with the in-kernel version, during 
+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: Version 2.9
 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)
@@ -215,22 +284,21 @@
 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.
+Cabling problems or line-downs are not detected, and only some 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 this point you can run a 'cat /dev/isdnctrl0' and view debugging messages.
 
-At the moment, debugging messages are enabled with the telesctrl tool:
+At the moment, debugging messages are enabled with the hisaxctrl tool:
 
-    telesctrl <DriverId> DebugCmd <debugging_flags>
+    hisaxctrl <DriverId> DebugCmd <debugging_flags>
 
-<DriverId> default is HiSax, if you didn't specified one.
+<DriverId> default is HiSax, if you didn't specify one.
 
 DebugCmd is  1  for generic debugging
             11  for layer 1 development debugging
@@ -241,54 +309,80 @@
 
 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)
+   0x0001  Link-level <--> hardware-level communication
+   0x0002  Top state machine
+   0x0004  D-Channel Frames for isdnlog
+   0x0008  D-Channel Q.921
+   0x0010  B-Channel X.75
+   0x0020  D-Channel l2
+   0x0040  B-Channel l2
+   0x0080  D-Channel link state debugging
+   0x0100  B-Channel link state debugging
+   0x0200  TEI debug
+   0x0400  LOCK debug in callc.c
+   0x0800  More paranoid debug in callc.c (not for normal use)
+   0x1000  D-Channel l1 state debugging
+   0x2000  B-Channel l1 state debugging
 
 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
+   0x0001  Warnings (default: on)
+   0x0002  IRQ status
+   0x0004  ISAC
+   0x0008  ISAC FIFO
+   0x0010  HSCX
+   0x0020  HSCX FIFO (attention: full B-Channel output!)
+   0x0040  D-Channel LAPD frame types
+   0x0080  IPAC debug
+   0x0100  HFC receive debug
+   0x0200  ISAC monitor debug
+   0x0400  D-Channel frames for isdnlog (set with 1 0x4 too)
+   0x0800  D-Channel message verbose
 
 With DebugCmd set to 13:
 
          1  Warnings (default: on)
-         2  l3 protocol discriptor errors
+         2  l3 protocol descriptor errors
          4  l3 state machine
          8  charge info debugging (1TR6)
 
-For example, 'telesctrl HiSax 1 0x3ff' enables full generic debugging.
+For example, 'hisaxctrl HiSax 1 0x3ff' enables full generic debugging.
+
+Because of some obscure problems with some switch equipment, the delay
+between the CONNECT message and sending the first data on the B-channel is now
+configurable with
+
+hisaxctrl <DriverId> 2 <delay>
+<delay> in ms Value between 50 and 800 ms is recommended.
+
+Downloading Firmware
+--------------------
+At the moment, the Sedlbauer speed fax+ is the only card, which
+needs to download firmware.
+The firmware is downloaded with the hisaxctrl tool:
+
+    hisaxctrl <DriverId> 9 <firmware_filename>
+
+<DriverId> default is HiSax, if you didn't specify one,
 
+where <firmware_filename> is the filename of the firmware file.
+
+For example, 'hisaxctrl HiSax 9 ISAR.BIN' downloads the firmware for
+ISAR based cards (like the Sedlbauer speed fax+).
 
 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.
-
+HiSax is a work in progress and may crash your machine.
+For certification look at HiSax.cert file.
 
 Limitations
 -----------
 At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines.
+For leased lines see appendix.
 
-
-Bugs 
+Bugs
 ----
-If you find any, please let me know. 
+If you find any, please let me know.
 
 
 Thanks
@@ -301,23 +395,208 @@
         Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en,
 	Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (ELSA GmbH),
 	Volker Schmidt
+	Edgar Toernig and Marcus Niemann for the Sedlbauer driver
+	Stephan von Krawczynski
+	Juergen Quade for the Leased Line part
+	Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE), for ELSA PCMCIA support
+	Enrik Berkhan (enrik@starfleet.inka.de) for S0BOX specific stuff
+	Ton van Rosmalen for Teles PCI
         and more people who are hunting bugs. (If I forgot somebody, please
-	send me a mail). 
+	send me a mail).
 
         Firma ELSA GmbH
-        
+        Firma Eicon.Diehl GmbH
+        Firma Dynalink NL
+	Firma ASUSCOM NETWORK INC. Taiwan
+	Firma S.u.S.E
+	Firma ith Kommunikationstechnik GmbH
+	Firma Traverse Technologie Australia
+	Firma Medusa GmbH  (www.medusa.de).
+	Firma Quant-X Austria for sponsoring a DEC Alpha board+CPU
+	Firma Cologne Chip Designs GmbH
+
         My girl friend and partner in life Ute for her patience with me.
 
 
 Enjoy,
 
-Karsten Keil   
-keil@temic-ech.spacenet.de
+Karsten Keil
+keil@isdn4linux.de
 
 
 Appendix: Teles PCMCIA driver
 -----------------------------
 
-See 
+See
    http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html
 for instructions.
+
+Appendix: Linux and ISDN-leased lines
+-------------------------------------
+
+Original from Juergen Quade, new version KKe.
+
+Attention NEW VERSION, the old leased line syntax won't work !!!
+
+You can use HiSax to connect your Linux-Box via an ISDN leased line
+to e.g. the Internet:
+
+1. Build a kernel which includes the HiSax driver either as a module
+   or as part of the kernel.
+     cd /usr/src/linux
+     make menuconfig
+     <ISDN subsystem - ISDN support -- HiSax>
+     make clean; make dep; make zImage; make modules; make modules_install
+2. Install the new kernel
+     cp /usr/src/linux/arch/i386/boot/zImage /etc/kernel/linux.isdn
+     vi /etc/lilo.conf
+     <add new kernel in the bootable image section>
+     lilo
+3. in case the hisax driver is a "fixed" part of the kernel, configure
+   the driver with lilo:
+     vi /etc/lilo.conf
+     <add HiSax driver parameter in the global section (see below)>
+     lilo
+   Your lilo.conf _might_ look like the following:
+
+	# LILO configuration-file
+	# global section
+    # teles 16.0 on IRQ=5, MEM=0xd8000, PORT=0xd80
+	append="hisax=1,3,5,0xd8000,0xd80,HiSax"
+    # teles 16.3 (non pnp) on IRQ=15, PORT=0xd80
+	# append="hisax=3,3,5,0xd8000,0xd80,HiSax"
+	boot=/dev/sda
+	compact        # faster, but won't work on all systems.
+	linear
+	read-only
+	prompt
+	timeout=100
+	vga = normal    # force sane state
+	# Linux bootable partition config begins
+	image = /etc/kernel/linux.isdn
+	root = /dev/sda1
+	label = linux.isdn
+	#
+	image = /etc/kernel/linux-2.0.30
+	root = /dev/sda1
+	label = linux.secure
+
+   In the line starting with "append" you have to adapt the parameters
+   according to your card (see above in this file)
+
+3. boot the new linux.isdn kernel
+4. start the ISDN subsystem:
+   a) load - if necessary - the modules (depends, whether you compiled
+      the ISDN driver as module or not)
+      According to the type of card you have to specify the necessary
+      driver parameter (irq, io, mem, type, protocol).
+      For the leased line the protocol is "3". See the table above for
+      the parameters, which you have to specify depending on your card.
+   b) configure i4l
+      /sbin/isdnctrl addif isdn0
+      # EAZ  1 -- B1 channel   2 --B2 channel
+      /sbin/isdnctrl eaz isdn0 1
+      /sbin/isdnctrl secure isdn0 on
+      /sbin/isdnctrl huptimeout isdn0 0
+      /sbin/isdnctrl l2_prot isdn0 hdlc
+      # Attention you must not set an outgoing number !!! This won't work !!!
+      # The incomming number is LEASED0 for the first card, LEASED1 for the
+      # second and so on.
+      /sbin/isdnctrl addphone isdn0 in LEASED0
+      # Here is no need to bind the channel.
+   c) in case the remote partner is a CISCO:
+      /sbin/isdnctrl encap isdn0 cisco-h
+   d) configure the interface
+      /sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
+   e) set the routes
+      /sbin/route add -host ${REMOTE_IP} isdn0
+      /sbin/route add default gw ${REMOTE_IP}
+   f) switch the card into leased mode for each used B-channel
+      /sbin/hisaxctrl HiSax 5 1
+
+Remarks:
+a) If you have a CISCO don't forget to switch off the KEEP ALIVE option!
+b) Use state of the art isdn4k-utils
+
+Here an example script:
+#!/bin/sh
+# Start/Stop ISDN lesaed line connection
+
+I4L_AS_MODULE=yes
+I4L_REMOTE_IS_CISCO=no
+I4L_MODULE_PARAMS="type=16 io=0x268 irq=7 "
+I4L_DEBUG=no
+I4L_LEASED_128K=yes
+LOCAL_IP=192.168.1.1
+REMOTE_IP=192.168.2.1
+
+case "$1" in
+    start)
+	echo "Starting ISDN ..."
+        if [ ${I4L_AS_MODULE} = "yes" ]; then
+		echo "loading modules..."
+		/sbin/modprobe hisax ${I4L_MODULE_PARAMS}
+	fi
+	# configure interface
+	/sbin/isdnctrl addif isdn0
+	/sbin/isdnctrl secure isdn0 on
+	if [ ${I4L_DEBUG} = "yes" ]; then
+		/sbin/isdnctrl verbose 7
+		/sbin/hisaxctrl HiSax 1 0xffff
+		/sbin/hisaxctrl HiSax 11 0xff
+		cat  /dev/isdnctrl >/tmp/lea.log &
+	fi
+	if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then
+		/sbin/isdnctrl encap isdn0 cisco-h
+	fi
+	/sbin/isdnctrl huptimeout isdn0 0
+	# B-CHANNEL 1
+	/sbin/isdnctrl eaz isdn0 1
+	/sbin/isdnctrl l2_prot isdn0 hdlc
+	# 1. card
+	/sbin/isdnctrl addphone isdn0 in LEASED0
+        if [ ${I4L_LEASED_128K} = "yes" ]; then
+		/sbin/isdnctrl addslave isdn0 isdn0s
+		/sbin/isdnctrl secure isdn0s on
+		/sbin/isdnctrl huptimeout isdn0s 0
+		# B-CHANNEL 2
+		/sbin/isdnctrl eaz isdn0s 2
+		/sbin/isdnctrl l2_prot isdn0s hdlc
+		# 1. card
+		/sbin/isdnctrl addphone isdn0s in LEASED0
+		if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then
+			/sbin/isdnctrl encap isdn0s cisco-h
+		fi
+	fi
+	/sbin/isdnctrl status isdn0 on
+	# configure tcp/ip
+	/sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
+	/sbin/route add -host ${REMOTE_IP} isdn0
+	/sbin/route add default gw ${REMOTE_IP}
+	# switch to leased mode
+	# B-CHANNEL 1
+	/sbin/hisaxctrl HiSax 5 1
+        if [ ${I4L_LEASED_128K} = "yes" ]; then
+		# B-CHANNEL 2
+		/sbin/hisaxctrl HiSax 5 2
+	fi
+	;;
+    stop)
+	/sbin/ifconfig isdn0 down
+	/sbin/isdnctrl delif isdn0
+	if [ ${I4L_DEBUG} = "yes" ]; then
+		killall cat
+	fi
+	if [ ${I4L_AS_MODULE} = "yes" ]; then
+		/sbin/rmmod hisax
+		/sbin/rmmod isdn
+		/sbin/rmmod ppp
+		/sbin/rmmod slhc
+	fi
+	;;
+    *)
+	echo "Usage: $0 {start|stop}"
+	exit 1
+esac
+exit 0
+
diff -u --recursive --new-file v2.0.35/linux/Documentation/isdn/README.audio linux/Documentation/isdn/README.audio
--- v2.0.35/linux/Documentation/isdn/README.audio	Mon Aug  4 17:33:59 1997
+++ linux/Documentation/isdn/README.audio	Sun Nov 15 10:32:43 1998
@@ -1,4 +1,4 @@
-$Id: README.audio,v 1.5 1997/02/23 23:53:46 fritz Exp $
+$Id: README.audio,v 1.5.2.1 1998/08/22 16:39:52 armin Exp $
 
 ISDN subsystem for Linux.
   Description of audio mode.
@@ -50,10 +50,11 @@
                          the application. See below for data format
 
         AT+VSD=x,y       Set silence-detection parameters.
-                         NO EFFECT, supported for compatibility
-                         only. Possible parameters:
-                           x = 0 ... 31
-                           y = 0 ... 255
+                         Possible parameters:
+                           x = 0 ... 31  sensitivity threshold level.
+                                         (default 0 , deactivated)
+                           y = 0 ... 255 range of interval in units
+                                         of 0.1 second. (default 70)
         AT+VSD=?         Report possible parameters.
         AT+VSD?          Show current parameters.
 
@@ -89,8 +90,8 @@
     <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><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.
          ...
@@ -102,13 +103,14 @@
     <DLE>C                  Touchtone "C" received.
     <DLE>D                  Touchtone "D" received.
 
+    <DLE>q                  quiet. Silence detected after non-silence.
+    <DLE>s                  silence. Silence detected from the
+                            start of recording.
+
     Currently unsupported DLE sequences:
 
     <DLE>c                  FAX calling tone received.
     <DLE>b                  busy tone received.
-    <DLE>q                  quiet. Silence detected after non-silence.
-    <DLE>s                  silence. Silence detected from the
-                            start of recording.
 
   Audio playback.
 
diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt
--- v2.0.35/linux/Documentation/networking/arcnet.txt	Sun Nov 15 10:49:25 1998
+++ linux/Documentation/networking/arcnet.txt	Sun Nov 15 10:32:44 1998
@@ -329,11 +329,10 @@
    	ifconfig arc0 insight
    	route add insight arc0
    	route add freedom arc0	/* I would use the subnet here (like I said
-					to to in "single protocol" above),
-   					but the rest of the subnet
-   					unfortunately lies across the PPP
-   					link on freedom, which confuses
-   					things. */
+					in "single protocol" above), but the
+					rest of the subnet unfortunately
+					lies across the PPP link on freedom,
+					which confuses things. */
    	route add default gw freedom
    	
    And freedom gets configured like so:
diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/rcpci45 linux/Documentation/networking/rcpci45
--- v2.0.35/linux/Documentation/networking/rcpci45	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/rcpci45	Sun Nov 15 10:32:44 1998
@@ -0,0 +1,85 @@
+
+
+Application Information
+
+The included application, called "rcc" (for RedCreek Control), is an 
+example of a user-space application (i.e., not running within kernel
+space).  It issues ioctl commands to communicate with the PCI driver.  
+It is intended for reference only.  It can currently report any of 
+the following information:
+
+   - PCI driver information ("getinfo")
+   - card statistics ("getstats")
+   - card's ip address & netmask ("getipnmask")
+   - card's mac address ("getmac")
+   - current speed ("getspeed")
+   - firmware version string ("getfirmware")
+   - status of the link (up or down) ("getstatus")
+
+This program needs to run as root, to avoid encountering permission 
+problems.  An alternative is to change the permission and ownership
+so that it runs as a setuid root process (for example, "chown 
+root.root rcc; chmod u+s rcc").
+
+
+Quick PCI driver background
+
+The adapter has its own IP and mac addresses which you have to
+assign using the RedCreek manager (assuming the adapter is
+running 3.X firmware).  Your linux box will not know anything 
+about the adapter's IP address -- ie, the adapter will show up 
+as a regular nic.  You will assign the linux box IP address using 
+the "ifconfig" command, as mentioned below.
+
+To compile the driver, simply type "make".  
+This, of course, assumes that you have GNU compiler environment
+already setup on a linux box.  The .c and .h files were copied 
+to a dos filesystem (the floppy), so you may have to use "dos2unix" to 
+convert it back to a unix text file.  Keep in mind that the driver
+currently works with kernels 2.0.X only.  Furthermore, it was only
+tested with kernel 2.0.34.  There is work being done on porting the
+driver to the 2.1.X kernel, however, it's still incomplete.
+
+When the compile is done, you'll send up with three object files
+related to the driver: rcmtl.o, rcpci45.o, and rc.o;  rcmtl.o
+is the RedCreek API message layer; rcpci45.o is the upper level
+Linux driver which contains the interface to the kernel.  Finally,
+since the driver is compiled as a loadable module, rcmtl.o and
+rcpci45.o are linked together into a third file, rc.o, which is the
+actual driver.  
+
+To load the driver:
+
+"insmod rc"
+
+If you are in console mode, you'll see a few messages send by the
+driver.  One of the messages will indicated how many adapters were
+found; the messages are also stored in /var/log/messages, so they
+can be viewed later.  If you are running X, then you'll have to 
+view the messages by examining /var/log/messages.
+
+The adapter will show up as a regular nic.  Thus, if you have only
+one nic (the pci card) in your box, you would at this point configure
+it with the following commands:
+
+mandatory:
+"ifconfig eth0 <your linux box IP address (NOT the IP address of the 
+                adapter!>"
+"route add -net <your network address> eth0"
+
+optional (if you want to be able to access other networks):
+"route add default gw <your default gateway IP address> eth0"
+
+Done.  Type "ifconfig" to see "eth0" and the packet count, as well
+as the IP address, net mask, etc.
+
+To unload the driver, you first have to shutdown the interface:
+
+"ifconfig eth0 down"
+
+Then you unload the driver with "rmmod rc".
+
+
+For technical support, please send email to Pete Popov at 
+ppopov@redcreek.com.  Please have as complete of a description of
+the problem as possible.
diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/shaper.txt linux/Documentation/networking/shaper.txt
--- v2.0.35/linux/Documentation/networking/shaper.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/shaper.txt	Sun Nov 15 10:32:44 1998
@@ -0,0 +1,49 @@
+Traffic Shaper For Linux
+
+This is the current ALPHA release of the traffic shaper for Linux. It works
+within the following limits:
+
+o	Minimum shaping speed is currently about 9600 baud (it can only
+shape down to 1 byte per clock tick)
+
+o	Maximum is about 256K, it will go above this but get a bit blocky.
+
+o	If you ifconfig the master device that a shaper is attached to down
+then your machine will follow.
+
+o	The shaper must be a module.
+
+
+Setup:
+
+	A shaper device is configured using the shapeconfig program.
+Typically you will do something like this
+
+shapecfg attach shaper0 eth1
+shapecfg speed shaper0 64000
+ifconfig shaper0 myhost netmask 255.255.255.240 broadcast 1.2.3.4.255 up
+route add -net some.network netmask a.b.c.d dev shaper0
+
+The shaper should have the same IP address as the device it is attached to
+for normal use.
+
+Gotchas:
+
+	The shaper shapes transmitted traffic. It's rather impossible to
+shape received traffic except at the end (or a router) transmitting it.
+
+	Gated/routed/rwhod/mrouted all see the shaper as an additional device
+and will treat it as such unless patched. Note that for mrouted you can run
+mrouted tunnels via a traffic shaper to control bandwidth usage.
+
+	The shaper is device/route based. This makes it very easy to use
+with any setup BUT less flexible. You may well want to combine this patch
+with Mike McLagan <mmclagan@linux.org>'s patch to allow routes to be
+specified by source/destination pairs.
+
+	There is no "borrowing" or "sharing" scheme. This is a simple
+traffic limiter. I'd like to implement Van Jacobson and Sally Floyd's CBQ
+architecture into Linux one day (maybe in 2.1 sometime) and do this with
+style.
+
+Alan
diff -u --recursive --new-file v2.0.35/linux/Documentation/networking/tlan.README linux/Documentation/networking/tlan.README
--- v2.0.35/linux/Documentation/networking/tlan.README	Sun Nov 15 10:49:25 1998
+++ linux/Documentation/networking/tlan.README	Sun Nov 15 10:32:44 1998
@@ -1,10 +1,11 @@
-TLAN driver for Linux, version 0.43
+TLAN driver for Linux, version 1.0
 README
 
-Note: I (James) am not maintaining this driver anymore, as I no longer
-	have the equipment to do so.  So it is available to anyone who
-	wishes to take it over ;)  If someone needs to reach me about
-	it, my new email address is james@sovereign.org.
+Well, I'm back.  The TLAN driver seems pretty stable, so I'm
+declaring this cycle of development finished, and calling the
+driver 1.0.  I will, of course continue to work on improving
+the driver, and work towards a 2.0 release.
+
 
 
 I.  Supported Devices.
diff -u --recursive --new-file v2.0.35/linux/Documentation/paride.txt linux/Documentation/paride.txt
--- v2.0.35/linux/Documentation/paride.txt	Sun Nov 15 10:49:25 1998
+++ linux/Documentation/paride.txt	Sun Nov 15 10:32:44 1998
@@ -2,13 +2,13 @@
 		Linux and parallel port IDE devices
 
 
-PARIDE-2.0.35   (c) 1997-8  Grant Guenther <grant@torque.net>
+PARIDE-1.03s   (c) 1997-8  Grant Guenther <grant@torque.net>
 
 *************************************************************************
 
-Special notes for the 2.0.35 version:
+Special notes for the 2.0 version:
 
-(i)   This is the paride from 2.1.107 retrofitted to work with 2.0.34.
+(i)   This is the paride from 2.1 retrofitted to work with 2.0.
      
 (ii)  PARPORT is _not_ supported.  If you obtain the PARPORT patches
       for 2.0 and try to use them, it might work.  I have not tried
@@ -20,6 +20,9 @@
 (iv)  I have not built or tested PARIDE with SMP support in 2.0.35,
       use it at your own risk.
 
+(v)   The pcd driver in 2.1 now contains support for playing audio 
+      discs.  This functionality is not available in the 2.0 version.
+
 *************************************************************************
 
 1. Introduction
@@ -61,8 +64,8 @@
 	SyQuest EZ-135, EZ-230 & SparQ drives
 	Avatar Shark
 	Imation Superdisk LS-120
-	FreeCom Power CD
-	Hewlett-Packard 5GB tape drive
+	FreeCom Power CD 
+	Hewlett-Packard 5GB and 8GB tape drives
 	Hewlett-Packard 7100 and 7200 CD-RW drives
 
 as well as most of the clone and no-name products on the market.
@@ -71,7 +74,7 @@
 subsystem, is actually structured in three parts.   There is a base
 paride module which provides a registry and some common methods for
 accessing the parallel ports.  The second component is a set of 
-high-level drivers for each of the different type of supported device: 
+high-level drivers for each of the different types of supported devices: 
 
 	pd	IDE disk
 	pcd	ATAPI CD-ROM
@@ -184,7 +187,7 @@
 is the parallel port base address, the 0 is the protocol registration
 number and 36 is the chain ID.
 
-This (2.0.34) version of PARIDE does not support chained devices on the
+This special version of PARIDE does not support chained devices on the
 same parallel port.
 
 2.2  Loading and configuring PARIDE as modules
@@ -193,7 +196,7 @@
 if you use them as loadable kernel modules.   
 
 Note:  using these drivers with the "kerneld" automatic module loading
-system is not recommended, and is not documented here.  
+system is not recommended for beginners, and is not documented here.  
 
 To use PARIDE, you must begin by 
 
@@ -303,13 +306,56 @@
 	mkdosfs /dev/pf0
 	mount /dev/pf0 /mnt
 
-2.4  Using the pg driver
+
+2.4  The pf driver
+
+The pf driver is intended for use with parallel port ATAPI disk
+devices.  The most common devices in this category are PD drives
+and LS-120 drives.  Traditionally, media for these devices are not
+partitioned.  Consequently, the pf driver does not support partitioned
+media.  This may be changed in a future version of the driver. 
+
+
+2.5  Using the pt driver
+
+The pt driver for parallel port ATAPI tape drives is a minimal driver.
+It does not yet support many of the standard tape ioctl operations. 
+For best performance, a block size of 32KB should be used.  You will
+probably want to set the parallel port delay to 0, if you can.
+
+
+2.6  Using the pg driver
 
 The pg driver can be used in conjunction with the cdrecord program
-to create CD-ROMs.  For more information, and the required patches 
-to cdrecord, please visit http://www.torque.net/parport/cdr.html .
+to create CD-ROMs.  Please get cdrecord version 1.6.1a3 or later
+from ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ (you may have to look
+in the alpha subdirectory).  To record CD-R media your parallel port
+should ideally be set to EPP mode, and the "port delay" should be
+set to 0.  With those settings it is possible to record at 2x speed
+without any buffer underruns.  If you cannot get the driver to work
+in EPP mode, try to use "bidirectional" or "PS/2" mode and 1x speeds only.
+
 
 3. Troubleshooting
+
+The most common problems that people report with the PARIDE drivers
+concern the parallel port CMOS settings.  At this time, none of the
+PARIDE protocol modules support ECP mode, or any ECP combination modes.
+If you are able to do so, please set your parallel port into EPP mode
+using your CMOS setup procedure.
+
+Some parallel ports cannot reliably transfer data at full speed.  To
+offset the errors, the PARIDE protocol modules introduce a "port
+delay" between each access to the i/o ports.  Each protocol sets
+a default value for this delay.  In most cases, the user can override
+the default and set it to 0 - resulting in somewhat higher transfer
+rates.  In some rare cases (especially with older 486 systems) the
+default delays are not long enough.  if you experience corrupt data
+transfers, or unexpected failures, you may wish to increase the
+port delay.   The delay can be programmed using the "driveN" parameters
+to each of the high-level drivers.  Please see the notes above, or
+read the comments at the beginning of the driver source files in
+linux/drivers/block/paride.
 
 While a lot of testing has gone into these drivers to make them work
 as smoothly as possible, problems will arise.  If you do have problems,
diff -u --recursive --new-file v2.0.35/linux/Documentation/specialix.txt linux/Documentation/specialix.txt
--- v2.0.35/linux/Documentation/specialix.txt	Tue Aug 12 11:17:41 1997
+++ linux/Documentation/specialix.txt	Sun Nov 15 10:32:44 1998
@@ -17,7 +17,10 @@
       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.
+      Functional Specification, augmented by document number 6200088:
+      Merak Hardware Functional Specification. (IO8+/PCI is also 
+      called Merak)
+
 
       This program is free software; you can redistribute it and/or
       modify it under the terms of the GNU General Public License as
@@ -44,10 +47,10 @@
 kernel sources?  And the manual of one of the boards in your computer?
 
 
-Adresses and interrupts
-=======================
+Addresses and interrupts
+========================
 
-Addres dip switch settings:
+Address dip switch settings:
 The dip switch sets bits 2-9 of the IO address. 
 
        9 8 7 6 5 4 3 2 
@@ -67,6 +70,9 @@
 other computer runs just fine with the Specialix card at 0x100....
 The card occupies 4 addresses, but actually only two are really used.
 
+The PCI version doesn't have any dip switches. The BIOS assigns
+an IO address. 
+
 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.
@@ -75,6 +81,9 @@
 change any jumpers to change the IRQ. Just use a command line
 argument (irq=xx) to the insmod program to set the interrupt.
 
+The BIOS assigns the IRQ on the PCI version. You have no say in what
+IRQ to use in that case. 
+
 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
@@ -99,6 +108,32 @@
 
 in your /etc/lilo.conf file if you use lilo. 
 
+The Specialix driver is slightly odd: It allows you to have the second
+or third card detected without having a first card. This has
+advantages and disadvantages. A slot that isn't filled by an ISA card,
+might be filled if a PCI card is detected. Thus if you have an ISA
+card at 0x250 and a PCI card, you would get:
+
+sx0: specialix IO8+ Board at 0x100 not found.
+sx1: specialix IO8+ Board at 0x180 not found.
+sx2: specialix IO8+ board detected at 0x250, IRQ 12, CD1865 Rev. B.
+sx3: specialix IO8+ Board at 0x260 not found.
+sx0: specialix IO8+ board detected at 0xd800, IRQ 9, CD1865 Rev. B.
+
+This would happen if you don't give any probe hints to the driver. 
+If you would specify:
+
+   specialix=0x250,11
+
+you'd get the following messages:
+
+sx0: specialix IO8+ board detected at 0x250, IRQ 11, CD1865 Rev. B.
+sx1: specialix IO8+ board detected at 0xd800, IRQ 9, CD1865 Rev. B.
+
+ISA probing is aborted after the IO address you gave is exhausted, and
+the PCI card is now detected as the second card. The ISA card is now
+also forced to IRQ11....
+
 
 Baud rates
 ==========
@@ -111,27 +146,33 @@
 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
+going to lose 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.
+miss incoming characters. The driver will print a warning message when
+you are outside the official specs. The messages usually show up in
+the file /var/log/messages .
 
 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
+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. 
 
+I upgraded the crystal to 66Mhz on one of my Specialix cards. Works
+great! Contact me for details. (Voids warranty, requires a steady hand
+and more such restrictions....)
+
 
 (*) Cirrus logic CD1864 databook, page 40.
 
@@ -248,7 +289,7 @@
 Ports and devices
 =================
 
-Port 0 is the one furthest from the ISA connector.
+Port 0 is the one furthest from the card-edge connector.
 
 Devices:
 
@@ -265,10 +306,20 @@
 done
 echo ""
 
+If your system doesn't come with these devices preinstalled, bug your
+linux-vendor about this. They have had ample time to get this
+implemented by now.
 
 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)....
+
+If you have enough PCI slots, you can probably use more than 4 PCI
+versions of the card though.... 
+
+The PCI version of the card cannot adhere to the mechanical part of
+the PCI spec because the 8 serial connectors are simply too large. If
+it doesn't fit in your computer, bring back the card.
 
 
 ------------------------------------------------------------------------
diff -u --recursive --new-file v2.0.35/linux/MAINTAINERS linux/MAINTAINERS
--- v2.0.35/linux/MAINTAINERS	Sun Nov 15 10:49:26 1998
+++ linux/MAINTAINERS	Sun Nov 15 10:32:44 1998
@@ -237,8 +237,10 @@
 S:	Maintained
 
 IDE/ATAPI CDROM DRIVER 
-P:	Erik Andersen
-M:	andersee@debian.org
+P:	Jens Axboe
+M:	axboe@image.dk
+P:	Chris Zwilling
+M:	chris@cloudnet.com
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
@@ -248,10 +250,18 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-ISDN SUBSYSTEM
+ISDN SUBSYSTEM [GENERAL]
 P:	Fritz Elfert
-M:	fritz@wuemaus.franken.de
+M:	fritz@isdn4linux.de
 L:	isdn4linux@hub-wue.franken.de
+W:	http://www.isdn4linux.de
+S:	Maintained
+
+ISDN SUBSYSTEM HISAX
+P:	Karsten Keil
+M:	keil@isdn4linux.de
+L:	isdn4linux@hub-wue.franken.de
+W:	http://www.isdn4linux.de
 S:	Maintained
 
 MODULE SUPPORT [GENERAL], KERNELD
diff -u --recursive --new-file v2.0.35/linux/Makefile linux/Makefile
--- v2.0.35/linux/Makefile	Sun Nov 15 10:49:26 1998
+++ linux/Makefile	Sun Nov 15 10:32:44 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 35
+SUBLEVEL = 36
 
 ARCH = i386
 
@@ -367,6 +367,9 @@
 endif
 
 depend dep: dep-files $(MODVERFILE)
+
+checkconfig:
+	perl -w scripts/checkconfig.pl `find * -name '*.[hcS]' -print | sort`
 
 ifdef CONFIGURATION
 ..$(CONFIGURATION):
diff -u --recursive --new-file v2.0.35/linux/Rules.make linux/Rules.make
--- v2.0.35/linux/Rules.make	Fri Sep 20 07:00:33 1996
+++ linux/Rules.make	Sun Nov 15 10:32:44 1998
@@ -22,11 +22,13 @@
 unexport O_OBJS
 unexport L_OBJS
 unexport M_OBJS
+unexport MI_OBJS
 unexport ALL_MOBJS
 # objects that export symbol tables
 unexport OX_OBJS
 unexport LX_OBJS
 unexport MX_OBJS
+unexport MIX_OBJS
 unexport SYMTAB_OBJS
 
 unexport MOD_LIST_NAME
@@ -103,7 +105,7 @@
 ifneq "$(strip $(ALL_MOBJS))" ""
 PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
 endif
-modules: $(ALL_MOBJS) dummy
+modules: $(ALL_MOBJS) $(MIX_OBJS) $(MI_OBJS) dummy
 ifdef MOD_SUB_DIRS
 	set -e; for i in $(MOD_SUB_DIRS); do $(MAKE) -C $$i modules; done
 endif
@@ -141,7 +143,7 @@
 # Exporting objects are: all objects that define symbol tables
 #
 ifdef CONFIG_MODVERSIONS
-SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS)
+SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) $(MIX_OBJS)
 ifneq "$(strip $(SYMTAB_OBJS))" ""
 
 MODINCL = $(TOPDIR)/include/linux/modules
@@ -152,6 +154,8 @@
 	$(CC) $(CFLAGS) -E -D__GENKSYMS__ $< | /sbin/genksyms $(MODINCL)
 
 $(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver)): $(TOPDIR)/include/linux/autoconf.h
+
+.PHONY: $(TOPDIR)/include/linux/modversions.h
 
 $(TOPDIR)/include/linux/modversions.h: $(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver))
 	@echo updating $(TOPDIR)/include/linux/modversions.h
diff -u --recursive --new-file v2.0.35/linux/arch/alpha/boot/bootp.c linux/arch/alpha/boot/bootp.c
--- v2.0.35/linux/arch/alpha/boot/bootp.c	Mon Jul 13 13:46:24 1998
+++ linux/arch/alpha/boot/bootp.c	Sun Nov 15 10:32:45 1998
@@ -11,7 +11,6 @@
 #include <linux/string.h>
 #include <linux/version.h>
 #include <linux/mm.h>
-#include <linux/config.h>
 
 #include <asm/system.h>
 #include <asm/console.h>
diff -u --recursive --new-file v2.0.35/linux/arch/alpha/kernel/lca.c linux/arch/alpha/kernel/lca.c
--- v2.0.35/linux/arch/alpha/kernel/lca.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/alpha/kernel/lca.c	Sun Nov 15 10:32:45 1998
@@ -455,7 +455,7 @@
 	      case MCHK_K_OS_BUGCHECK:	reason = "callsys in kernel mode"; break;
 	      case MCHK_K_DCPERR:	reason = "d-cache parity error"; break;
 	      case MCHK_K_ICPERR:	reason = "i-cache parity error"; break;
-	      case MCHK_K_SIO_SERR:	reason = "SIO SERR occurred on on PCI bus"; break;
+	      case MCHK_K_SIO_SERR:	reason = "SIO SERR occurred on PCI bus"; break;
 	      case MCHK_K_SIO_IOCHK:	reason = "SIO IOCHK occurred on ISA bus"; break;
 	      case MCHK_K_DCSR:		reason = "MCHK_K_DCSR"; break;
 	      case MCHK_K_UNKNOWN:
diff -u --recursive --new-file v2.0.35/linux/arch/alpha/kernel/smc.c linux/arch/alpha/kernel/smc.c
--- v2.0.35/linux/arch/alpha/kernel/smc.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/alpha/kernel/smc.c	Sun Nov 15 10:32:45 1998
@@ -1967,7 +1967,7 @@
 **	the configuration is also updated.  If the device function
 **	is currently disabled, only the local shadow copy is 
 **	updated and the actual device function will be updated
-**	if/when is is enabled.
+**	if/when it is enabled.
 **
 **--
 */
diff -u --recursive --new-file v2.0.35/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
--- v2.0.35/linux/arch/alpha/kernel/traps.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/alpha/kernel/traps.c	Sun Nov 15 10:32:45 1998
@@ -422,7 +422,7 @@
 {
 	if (regs.r0 != 112 && regs.r0 < 300)
 		printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2);
-	return -1;
+	return -ENOSYS;
 }
 
 extern asmlinkage void entMM(void);
diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/compressed/head.S linux/arch/i386/boot/compressed/head.S
--- v2.0.35/linux/arch/i386/boot/compressed/head.S	Sat Mar  9 03:31:42 1996
+++ linux/arch/i386/boot/compressed/head.S	Sun Nov 15 10:32:45 1998
@@ -28,7 +28,6 @@
 .text
 
 #define __ASSEMBLY__
-#include <linux/config.h>
 #include <linux/linkage.h>
 #include <asm/segment.h>
 
diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c
--- v2.0.35/linux/arch/i386/boot/compressed/misc.c	Tue Dec  2 13:52:31 1997
+++ linux/arch/i386/boot/compressed/misc.c	Sun Nov 15 10:32:45 1998
@@ -97,6 +97,9 @@
  * This is set up by the setup-routine at boot-time
  */
 #define EXT_MEM_K (*(unsigned short *)0x90002)
+#ifndef STANDARD_MEMORY_BIOS_CALL
+#define ALT_MEM_K (*(unsigned long *) 0x901e0)
+#endif
 #define DRIVE_INFO (*(struct drive_info *)0x90080)
 #define SCREEN_INFO (*(struct screen_info *)0x90000)
 #define RAMDISK_SIZE (*(unsigned short *)0x901F8)
@@ -313,7 +316,11 @@
 
 void setup_normal_output_buffer()
 {
+#ifdef STANDARD_MEMORY_BIOS_CALL
 	if (EXT_MEM_K < 1024) error("Less than 2MB of memory.\n");
+#else
+	if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) error("Less than 2MB of memory.\n");
+#endif
 	output_data = (char *)0x100000; /* Points to 1M */
 }
 
@@ -325,7 +332,11 @@
 void setup_output_buffer_if_we_run_high(struct moveparams *mv)
 {
 	high_buffer_start = (uch *)(((ulg)&end) + HEAP_SIZE);
+#ifdef STANDARD_MEMORY_BIOS_CALL
 	if (EXT_MEM_K < (3*1024)) error("Less than 4MB of memory.\n");
+#else
+	if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3*1024)) error("Less than 4MB of memory.\n");
+#endif
 	mv->low_buffer_start = output_data = (char *)LOW_BUFFER_START;
 	high_loaded = 1;
 	free_mem_end_ptr = (long)high_buffer_start;
diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v2.0.35/linux/arch/i386/boot/setup.S	Sat Mar 30 10:58:57 1996
+++ linux/arch/i386/boot/setup.S	Sun Nov 15 10:32:45 1998
@@ -232,6 +232,29 @@
 loader_ok:
 ! Get memory size (extended mem, kB)
 
+#ifndef STANDARD_MEMORY_BIOS_CALL
+	push	ebx
+
+        xor     ebx,ebx		! preload new memory slot with 0k
+        mov	[0x1e0], ebx
+
+        mov     ax,#0xe801
+	int     0x15
+	jc      oldstylemem
+
+	and     ebx, #0xffff    ! clear sign extend
+	shl     ebx, 6          ! and go from 64k to 1k chunks
+	mov     [0x1e0],ebx     ! store extended memory size
+
+	and     eax, #0xffff    ! clear sign extend
+ 	add     [0x1e0],eax     ! and add lower memory into total size.
+  
+	! and fall into the old memory detection code to populate the
+	! compatability slot.
+
+oldstylemem:
+	pop	ebx
+#endif
 	mov	ah,#0x88
 	int	0x15
 	mov	[2],ax
diff -u --recursive --new-file v2.0.35/linux/arch/i386/boot/tools/build.c linux/arch/i386/boot/tools/build.c
--- v2.0.35/linux/arch/i386/boot/tools/build.c	Wed Jul 10 03:10:59 1996
+++ linux/arch/i386/boot/tools/build.c	Sun Nov 15 10:32:46 1998
@@ -30,9 +30,10 @@
 #include <sys/sysmacros.h>
 #include <unistd.h>	/* contains read/write */
 #include <fcntl.h>
-#include <linux/a.out.h>
 #include <linux/config.h>
+#include <linux/a.out.h>
 #include <errno.h>
+
 
 #define MINIX_HEADER 32
 
diff -u --recursive --new-file v2.0.35/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.0.35/linux/arch/i386/config.in	Sun May 12 21:17:23 1996
+++ linux/arch/i386/config.in	Sun Nov 15 10:32:46 1998
@@ -43,6 +43,12 @@
 	 486		CONFIG_M486	\
 	 Pentium	CONFIG_M586	\
 	 PPro		CONFIG_M686" Pentium
+
+# Conditionally compile MTRR manipulation support
+if [ "$CONFIG_M686" = "y" -o "$CONFIG_M586" = "y" ]; then
+  bool 'Handle buggy SMP BIOSes with bad MTRR setup' CONFIG_MTRR
+fi
+
 endmenu
 
 source drivers/block/Config.in
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- v2.0.35/linux/arch/i386/kernel/Makefile	Tue Sep 16 14:33:59 1997
+++ linux/arch/i386/kernel/Makefile	Sun Nov 15 10:32:46 1998
@@ -26,6 +26,10 @@
 
 ifdef SMP
 
+ifdef CONFIG_MTRR
+O_OBJS += mtrr.o
+endif
+
 O_OBJS += smp.o
 
 head.o: head.S $(TOPDIR)/include/linux/tasks.h
@@ -62,6 +66,6 @@
 	$(CPP) -D__SMP__ -traditional $< -o $@
 
 clean:
-	rm -f trampoline hexify
+	rm -f trampoline trampoline.hex hexify
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.0.35/linux/arch/i386/kernel/entry.S	Sun Nov 15 10:49:27 1998
+++ linux/arch/i386/kernel/entry.S	Sun Nov 15 10:32:46 1998
@@ -699,5 +699,12 @@
 	.long SYMBOL_NAME(sys_nanosleep)
 	.long SYMBOL_NAME(sys_mremap)
 	.long 0,0
-	.long SYMBOL_NAME(sys_vm86)
-	.space (NR_syscalls-166)*4
+	.long SYMBOL_NAME(sys_vm86)		/* 166 */
+	.long 0					/* 167 */
+	.long 0					/* 168 STREAMS poll */
+	.long 0					/* 169 */
+	.long 0,0,0,0,0,0,0,0,0,0		/* 170 - 179 */
+	.long 0,0,0,0,0,0,0,0			/* 180 - 187 */
+	.long 0					/* 188 STREAMS getpmsg */
+	.long 0					/* 189 STREAMS putpmsg */
+	.space (NR_syscalls-189)*4
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.0.35/linux/arch/i386/kernel/head.S	Sun Nov 15 10:49:27 1998
+++ linux/arch/i386/kernel/head.S	Sun Nov 15 10:32:46 1998
@@ -216,15 +216,16 @@
 	orb $0x10,%bx		# by setting bit 4
 	setCx86($0xc3,%bx)
 
+        getCx86($0xfe)          # DIR0 : let's check this is a 6x86(L)
+        andb $0xf0,%ax		# should be 3xh
+	cmpb $0x30,%ax		# 
+	jne n6x86
+
 	getCx86($0xe8)		# now we can get CCR4
 	orb $0x80,%ax		# and set bit 7 (CPUIDEN)
 	movb %ax,%bx		# to enable CPUID execution
 	setCx86($0xe8,%bx)
 
-        getCx86($0xfe)          # DIR0 : let's check this is a 6x86(L)
-        andb $0xf0,%ax		# should be 3xh
-	cmpb $0x30,%ax		# 
-	jne n6x86
         getCx86($0xe9)          # CCR5 : we reset the SLOP bit
         andb $0xfd,%ax		# so that udelay calculation
         movb %ax,%bx		# is correct on 6x86(L) CPUs
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.0.35/linux/arch/i386/kernel/irq.c	Mon Mar 17 14:58:59 1997
+++ linux/arch/i386/kernel/irq.c	Sun Nov 15 10:32:46 1998
@@ -345,7 +345,29 @@
 {
 	struct irqaction * action = *(irq + irq_action);
 	int do_random = 0;
-
+	int c,intm,mask;
+#ifdef IRQ_DEBUG
+	static int count;
+	if (smp_processor_id() != 0 && count++ < 1000)
+	  printk("IRQ %d: done by CPU %d\n",irq,smp_processor_id());
+#endif	  
+	if (irq  >= 8) {
+	  c = cache_A1;
+	  intm = inb(0xA1);
+	  mask =  1 << (irq - 8);
+	} else {
+	  c = cache_21;
+	  intm = inb(0x21);
+	  mask =  1 << irq;
+	}
+	if (!(c & mask) || !(intm & mask)) {
+#ifdef IRQ_DEBUG	
+	  printk("IRQ %d (proc %d):cache_x1=0x%x,INT mask=0x%x\n", irq, smp_processor_id(),c,intm);
+#endif	  
+	  /* better to return because the interrupt may be asserted again,
+	     the bad thing is that we may loose some interrupts */
+	  return;
+	}
 #ifdef __SMP__
 	if(smp_threads_ready && active_kernel_processor!=smp_processor_id())
 		panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/ksyms.c linux/arch/i386/kernel/ksyms.c
--- v2.0.35/linux/arch/i386/kernel/ksyms.c	Sun Nov 15 10:49:27 1998
+++ linux/arch/i386/kernel/ksyms.c	Sun Nov 15 10:32:46 1998
@@ -6,6 +6,7 @@
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
+#include <asm/ptrace.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(elf_fpregset_t *);
@@ -16,6 +17,7 @@
 	X(x86_capability),
 	X(dump_thread),
 	X(dump_fpu),
+	X(get_pt_regs_for_task),
 	XNOVERS(__do_delay),
 	XNOVERS(down_failed),
 	XNOVERS(down_failed_interruptible),
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- v2.0.35/linux/arch/i386/kernel/mtrr.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/kernel/mtrr.c	Sun Nov 15 10:32:46 1998
@@ -0,0 +1,602 @@
+/*
+ *      Read and write Memory Type Range Registers (MTRRs)
+ *
+ *      These machine specific registers contain information about
+ *      caching of memory regions on Intel processors.
+ *
+ *      This code has been derived from pform_mod.c by M. Tisch"auser
+ *      (email martin@ikcbarka.fzk.de). Special thanks to mingo for
+ *      his hint.
+ *
+ *      (c) 1997 M. Ohlenroth <moh@informatik.tu-chemnitz.de>
+ *      NO WARRANTY: use this code at your own risk!
+ *
+ *	This code is released under the GNU public license version 2 or
+ *	later.
+ *
+ *      modified to have a /proc/mtrr interface by M. Fr"ohlich, Jan. 1998
+ *      <frohlich@na.uni-tuebingen.de>
+ *         the user Interface is partly taken form mtrr-patch-v1.5
+ *      Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
+ *        The postal address is:
+ *      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/proc_fs.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/mtrr.h>
+
+#define MTRR_CAP	0x0fe	/* MTRRcap register */
+#define MTRR_VARIABLE	0x200	/* variable length registers */
+#define MTRR_FIXED64K	0x250	/* fixed size registers 64k */
+#define MTRR_FIXED16K	0x258	/* fixed size registers 16K */
+#define	MTRR_FIXED4K	0x268	/* fixed size registers 4K */
+#define MTRR_DEFTYPE	0x2ff	/* MTRRdefType register */
+
+/*
+ * data type for the MTRRcap register
+ */
+typedef struct {
+	__u64   VCNT          :  8  __attribute__ ((packed)),
+		FIX           :  1  __attribute__ ((packed)),
+		__reserved_2  :  1  __attribute__ ((packed)),
+		WC            :  1  __attribute__ ((packed)),
+		__reserved_1  :  53 __attribute__ ((packed));
+} MTRRcap_t __attribute__ ((packed));
+
+
+/*
+ * data type for the MTRRdefType register
+ */
+typedef struct {
+	__u64   Type          :  8  __attribute__ ((packed)),
+		__reserved_1  :  2  __attribute__ ((packed)),
+		FE            :  1  __attribute__ ((packed)),
+		E             :  1  __attribute__ ((packed)),
+		__reserved_2  :  52 __attribute__ ((packed));
+} MTRRdefType_t __attribute__ ((packed));
+
+/* FIXME implement the entry struct */
+typedef struct MTRRfix64K_t {
+	__u64   raw;
+} MTRRfix64K_t __attribute__ ((packed));
+
+typedef struct MTRRfix16K_t {
+	__u64   raw;
+} MTRRfix16K_t __attribute__ ((packed));
+
+typedef struct MTRRfix4K_t {
+	__u64   raw;
+} MTRRfix4K_t __attribute__ ((packed));
+
+/*
+ * data type for a pair of variable MTRR registers
+ */
+typedef struct {
+	struct {
+		__u64   Type          :  8  __attribute__ ((packed)),
+			__reserved_1  :  4  __attribute__ ((packed)),
+			PhysBase      :  24 __attribute__ ((packed)),
+			__reserved_2  :  28 __attribute__ ((packed));
+	} MTRRphysBase  __attribute__ ((packed));
+
+	struct {
+		__u64   __reserved_3  :  11 __attribute__ ((packed)),
+			V             :  1  __attribute__ ((packed)),
+			PhysMask      :  24 __attribute__ ((packed)),
+			__reserved_4  :  28 __attribute__ ((packed));
+	} MTRRphysMask __attribute__ ((packed));
+} MTRRvar_t __attribute__ ((packed));
+
+#define RAW_ACCESS64(data) (*(unsigned long long *)(&(data)))
+
+/*
+ * MTRR configuration struct
+ */
+struct mtrr_cntl_t {
+	MTRRcap_t      MTRRcap;     /* MTRR capability register */
+	MTRRdefType_t  MTRRdefType; /* MTRR default type register */
+	MTRRfix64K_t   fixed64;     /* fixed length entries (raw data) */
+	MTRRfix16K_t   fixed16[2];
+	MTRRfix4K_t    fixed4[8];
+	MTRRvar_t      variable[0]; /* variable type entries */
+};
+
+static struct mtrr_cntl_t *mtrrcntl      = NULL;
+
+/*
+ * Deletes the variable MTRR *MTRRvar
+ */
+static inline void MTRRvar_delete(MTRRvar_t *MTRRvar)
+{
+	RAW_ACCESS64(MTRRvar->MTRRphysBase) = 0;
+	RAW_ACCESS64(MTRRvar->MTRRphysMask) = 0;
+}
+
+
+/*
+ * Sets the variable MTRR *MTRRvar
+ */
+static inline void MTRRvar_set(MTRRvar_t *MTRRvar, unsigned int type,
+			       unsigned long base, unsigned long size)
+{
+	unsigned long val;
+	base >>= 12;
+	size >>= 12;
+	
+	MTRRvar->MTRRphysBase.Type = type;
+	MTRRvar->MTRRphysBase.PhysBase = base;
+	
+	MTRRvar->MTRRphysMask.V = 1;
+	val = 1<<25;
+	while (0 == (val & size)) val |= (val>>1);
+	MTRRvar->MTRRphysMask.PhysMask = val;
+}
+
+
+/*
+ * returns 1 if the variable MTRR entry *MTRRvar is valid, 0 otherwise
+ */
+static inline int MTRRvar_is_valid(const MTRRvar_t *MTRRvar)
+{
+	return MTRRvar->MTRRphysMask.V;
+}
+
+/*
+ * returns the type of the variable MTRR entry *MTRRvar
+ */
+static inline int MTRRvar_get_type(const MTRRvar_t *MTRRvar)
+{
+	return MTRRvar->MTRRphysBase.Type;
+}
+
+/*
+ * returns the base of the variable MTRR entry *MTRRvar
+ */
+static inline unsigned long long MTRRvar_get_base(const MTRRvar_t *MTRRvar)
+{
+	return ((unsigned long long)MTRRvar->MTRRphysBase.PhysBase) << 12;
+}
+
+/*
+ * returns the size of the variable MTRR entry *MTRRvar
+ */
+static inline unsigned long long MTRRvar_get_size(const MTRRvar_t *MTRRvar)
+{
+	if (MTRRvar->MTRRphysMask.PhysMask == 0) {
+		return 0;
+	} else {
+		unsigned long size = 1;
+		const unsigned long Mask = MTRRvar->MTRRphysMask.PhysMask;
+		while (0 == (Mask & size)) size <<= 1;
+		return ((unsigned long long)size) << 12;
+	}
+}
+
+/*
+ *  returns the eflags register
+ */
+static inline int read_eflags(void)
+{
+	int ret;
+	asm volatile (
+		"pushfl\n\t"
+		"popl %%eax\n\t"
+		:"=a" (ret)
+		:
+		);
+	return ret;
+}
+
+/*
+ *  writes the eflags register
+ */
+static inline void write_eflags(int flag)
+{
+	asm volatile (
+		"pushl %%eax\n\t"
+		"popfl\n\t"
+		:
+		:"a" (flag)
+		);
+}
+
+/*
+ * returns 1 if the mtrr's are supported by the current processor, 0 otherwise
+ */
+static inline int mtrr_detect(void) {
+	unsigned long flags;
+	int eflags;
+	int val;
+	
+#define MSR_MASK 0x20
+#define MTRR_MASK 0x1000
+#define CPUID_MASK 0x200000
+	/* this function may be called before the cpu_data array has
+		been initialized */
+	save_flags(flags); sti();
+	eflags = read_eflags();
+	write_eflags(eflags ^ CPUID_MASK);
+	if (!((eflags ^ read_eflags()) & CPUID_MASK)) {
+		write_eflags(eflags);
+		restore_flags(flags);
+		return 0;
+	}
+	write_eflags(eflags);
+	restore_flags(flags);
+
+	/* get the cpuid level */
+	asm volatile (
+		"xorl %%eax,%%eax\n\t"
+		"cpuid"
+		:"=a"(val)::"ebx","ecx","edx"
+	);
+	if (val < 1) return 0;
+	/* get the x86_capability value */
+	asm volatile (
+		"movl $1,%%eax\n\t"
+		"cpuid"
+		:"=d"(val)::"ebx","ecx","eax"
+	);
+	if (!(val & MSR_MASK)) return 0;
+	if (!(val & MTRR_MASK)) return 0;
+
+	return 1;
+#undef MSR_MASK
+#undef MTRR_MASK
+#undef CPUID_MASK
+}
+
+
+/*
+ * reads the mtrr configuration of the actual processor and returns
+ * this configuration on sucess. returns NULL if an error occured or 
+ * if mtrr's are not supported.
+ */
+static struct mtrr_cntl_t *read_mtrr_configuration (void) {
+	struct mtrr_cntl_t *mtrrcntl;
+	int i;
+	size_t size;
+	MTRRcap_t MTRRcap;
+
+	if (!mtrr_detect()) {
+		printk("/proc/mtrr: MTRR's are NOT supported\n");
+		return NULL;
+	}
+
+	RAW_ACCESS64(MTRRcap) = rdmsr(MTRR_CAP);
+
+/* #define DUMP_MTRR */
+#ifdef DUMP_MTRR
+	{
+		/* Written for a bugreport to Gigabyte ... */
+		inline void print_msr(int num) {
+			unsigned long long tmp = rdmsr(num);
+			printk("MSR #%#06x: 0x%08lx%08lx\n",
+			       num , (unsigned long)(tmp >> 32),
+			       (unsigned long)tmp);
+		}
+		
+		print_msr(MTRR_CAP);
+		/* all variable type */
+		for (i=0;i < MTRRcap.VCNT;i++) {
+			print_msr(MTRR_VARIABLE+2*i);
+			print_msr(MTRR_VARIABLE+2*i+1);
+		}
+		/* all fixed type */
+		print_msr(MTRR_FIXED64K);
+		print_msr(MTRR_FIXED16K);
+		print_msr(MTRR_FIXED16K+1);
+		for (i=0;i<8;i++)
+			print_msr(MTRR_FIXED4K+i);
+		print_msr(MTRR_DEFTYPE);
+	}
+#endif
+	
+	size = sizeof(struct mtrr_cntl_t) + sizeof(MTRRvar_t)*MTRRcap.VCNT;
+	if (NULL == (mtrrcntl = kmalloc(size, GFP_KERNEL))) return NULL;
+	memset(mtrrcntl, 0, size);
+
+	/* read MTRRcap register */
+	mtrrcntl->MTRRcap = MTRRcap;
+
+	/* read MTRRdefType register */
+	RAW_ACCESS64(mtrrcntl->MTRRdefType) = rdmsr(MTRR_DEFTYPE);
+
+	/* read fixed length entries */
+	if (mtrrcntl->MTRRdefType.E && mtrrcntl->MTRRdefType.FE) {
+		mtrrcntl->fixed64.raw = rdmsr(MTRR_FIXED64K);
+		mtrrcntl->fixed16[0].raw = rdmsr(MTRR_FIXED16K);
+		mtrrcntl->fixed16[1].raw = rdmsr(MTRR_FIXED16K+1);
+		for (i=0;i<8;i++)
+			mtrrcntl->fixed4[i].raw = rdmsr(MTRR_FIXED4K+i);
+	}
+
+	/* read variable length entries */
+	if (mtrrcntl->MTRRdefType.E) {
+		const int vcnt = mtrrcntl->MTRRcap.VCNT;
+		for (i = 0 ; i < vcnt ; i++) {
+			RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) =
+				rdmsr(MTRR_VARIABLE + 2*i);
+			RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) =
+				rdmsr(MTRR_VARIABLE + 2*i + 1);
+		}
+	}
+
+	return mtrrcntl;
+}
+
+/*
+ * initializes the global mtrr configuration
+ */
+/*__init_function(void init_mtrr_config(void)) FIXME*/
+void init_mtrr_config(void)
+{
+	mtrrcntl = read_mtrr_configuration();
+}
+
+
+/* write back and invalidate cache */
+static inline void wbinvd(void)
+{
+	asm volatile("wbinvd");
+}
+
+/* flush tlb's */
+static inline void flush__tlb(void)
+{
+	asm volatile (
+		"movl  %%cr3, %%eax\n\t"
+		"movl  %%eax, %%cr3\n\t"
+		:
+		:
+		: "memory", "eax");
+}
+
+/* clear page global enable and return previous value */
+static inline unsigned long clear_pge(void)
+{
+	unsigned long ret;
+	asm volatile (
+		"movl  %%cr4, %%eax\n\t"
+		"movl  %%eax, %%edx\n\t"
+		"andl  $0x7f, %%edx\n\t"
+		"movl  %%edx, %%cr4\n\t"
+		: "=a" (ret)
+		:
+		: "memory", "cc", "eax", "edx");
+	return ret;
+}
+
+/* restores page global enable bit */
+static inline void restore_pge(unsigned long cr4)
+{
+	asm volatile (
+		"movl  %0, %%cr4\n\t"
+		:
+		: "r" (cr4)
+		: "memory");
+}
+
+/* ... */
+static inline void disable_cache(void)
+{
+	asm volatile (
+		"movl  %%cr0, %%eax\n\t"
+		"orl   $0x40000000, %%eax\n\t"
+		"movl  %%eax, %%cr0\n\t"
+		:
+		:
+		:"memory", "cc", "eax");
+}
+
+/* ... */
+static inline void enable_cache(void)
+{
+	asm volatile (
+		"movl  %%cr0, %%eax\n\t"
+		"andl  $0xbfffffff, %%eax\n\t"
+		"movl  %%eax, %%cr0"
+		:
+		:
+		:"memory", "cc", "eax");
+}
+
+/* clear the MTRRdefType.E and MTRRdefType.FE flag to disable these MTRR's */
+static inline void disable_mtrr(void)
+{
+	MTRRdefType_t MTRRdefType;
+
+	RAW_ACCESS64(MTRRdefType) = rdmsr(MTRR_DEFTYPE);
+	MTRRdefType.E = 0;
+	MTRRdefType.FE = 0;
+	wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType));
+}
+
+/*
+ * written from pseudocode from intel
+ *  (PentiumPro Family Developers manual Volume 3, P 322)
+ *  
+ */
+static inline unsigned long pre_mtrr_change(void)
+{
+	unsigned long cr4;
+
+	cr4 = clear_pge();
+
+	wbinvd();
+
+	disable_cache();
+
+	wbinvd();
+
+	flush__tlb();
+	
+	disable_mtrr();
+
+	return cr4;
+}
+
+/*
+ * written from pseudocode from intel
+ *  (PentiumPro Family Developers manual Volume 3, P 322)
+ */
+static inline void post_mtrr_change(MTRRdefType_t MTRRdefType,unsigned long cr4)
+{
+	wbinvd();
+
+	flush__tlb();
+
+	wrmsr(MTRR_DEFTYPE, RAW_ACCESS64(MTRRdefType));
+
+	enable_cache();
+
+	restore_pge(cr4);
+}
+
+/*
+ * writes all fixed mtrr's
+ */
+static inline void set_mtrr_fixed(void) {
+	int i;
+
+	wrmsr(MTRR_FIXED64K,mtrrcntl->fixed64.raw);
+	wrmsr(MTRR_FIXED16K+0,mtrrcntl->fixed16[0].raw);
+	wrmsr(MTRR_FIXED16K+1,mtrrcntl->fixed16[1].raw);
+	for (i=0;i<8;i++)
+		wrmsr(MTRR_FIXED4K+i,mtrrcntl->fixed4[i].raw);
+}
+
+/*
+ * writes all variable mtrr's
+ */
+static inline void set_mtrr_variable(void) {
+	int i;
+	const int vcnt = mtrrcntl->MTRRcap.VCNT;
+
+	for (i = 0 ; i < vcnt ; i++ ) {
+		wrmsr(MTRR_VARIABLE +2*i,
+		      RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase));
+		wrmsr(MTRR_VARIABLE +2*i+1,
+		      RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask));
+	}
+}
+
+
+/*
+ * compares the mtrr_cntl_t structure second with that set by
+ * the boot processor,
+ * returns 0 if equal,
+ *        -1 if the structs are not initialized,
+ *         1 if they are different
+ *
+ * the *second struct is assumed to be local, it is not locked!
+ */
+static inline int compare_mtrr_configuration(struct mtrr_cntl_t *second) {
+	int i, result = 0;
+
+	if (NULL == mtrrcntl) { result = -1; goto end; }
+	if (NULL == second) { result = -1; goto end; }
+	if (RAW_ACCESS64(mtrrcntl->MTRRcap)
+	    != RAW_ACCESS64(second->MTRRcap)) {
+		result = 1; goto end;
+	}
+
+	if (RAW_ACCESS64(mtrrcntl->MTRRdefType)
+	    != RAW_ACCESS64(second->MTRRdefType)) {
+		result = 1; goto end;
+	}
+
+	if (mtrrcntl->fixed64.raw != second->fixed64.raw) {
+		result = 1; goto end;
+	}
+	if (mtrrcntl->fixed16[0].raw != second->fixed16[0].raw) {
+		result = 1; goto end;
+	}
+	if (mtrrcntl->fixed16[1].raw != second->fixed16[1].raw) {
+		result = 1; goto end;
+	}
+
+	for (i=0;i<8;i++)
+		if (mtrrcntl->fixed4[i].raw != second->fixed4[i].raw) {
+			result = 1; goto end;
+		}
+	{
+		const int vcnt = mtrrcntl->MTRRcap.VCNT;
+		for (i = 0; i < vcnt; i++) {
+			if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysBase) !=
+			    RAW_ACCESS64(second->variable[i].MTRRphysBase)) {
+				result = 1; goto end;
+			}
+			if (RAW_ACCESS64(mtrrcntl->variable[i].MTRRphysMask) !=
+			    RAW_ACCESS64(second->variable[i].MTRRphysMask)) {
+				result = 1; goto end;
+			}
+		}
+	}
+
+	end:
+
+	return result;
+}
+
+/*
+ * compares the mtrr configuration of the current processor with the
+ * main configuration and overwrites the mtrr's in the processor if they
+ * differ. (fixes a bug in the GA-686DX mainboard BIOS)
+ */
+void check_mtrr_config(void) {
+	unsigned long cr4;
+	unsigned long flags;
+	struct mtrr_cntl_t *this_cpu_setting;
+	int result;
+
+	save_flags(flags); sti();
+
+	/* if global struct is not initialized return */
+	if (mtrrcntl == NULL) {
+		restore_flags(flags);
+		return;
+	}
+
+	/* disable MTRR feature if this_cpu_setting == NULL */
+	/* read mtrr configuration of this cpu */
+	this_cpu_setting = read_mtrr_configuration();
+	if (this_cpu_setting == NULL) {
+		printk("/proc/mtrr: MTRR's are NOT supported by cpu %i.\n",
+		       smp_processor_id());
+		restore_flags(flags);
+
+		return;
+	}
+	/* compare mtrr configuration */
+	result = compare_mtrr_configuration(this_cpu_setting);
+	kfree(this_cpu_setting);
+ 	/* return if mtrr setting is correct */
+	if (0 >= result) {
+		restore_flags(flags);
+		return;
+	}
+	
+	/* prepare cpu's for setting mtrr's */
+	cr4 = pre_mtrr_change();
+
+	/* set all mtrr's */
+ 	set_mtrr_fixed();
+ 	set_mtrr_variable();
+
+	/* prepare cpu's for running */
+	post_mtrr_change(mtrrcntl->MTRRdefType, cr4);
+
+	restore_flags(flags);
+
+	printk("\nBIOS bug workaround: MTRR configuration changed on cpu "
+	       "%i.\n", smp_processor_id());
+}
+
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.0.35/linux/arch/i386/kernel/setup.c	Sun Nov 15 10:49:27 1998
+++ linux/arch/i386/kernel/setup.c	Sun Nov 15 10:32:46 1998
@@ -42,15 +42,21 @@
 char x86_model = 0;		/* set by kernel/head.S */
 char x86_mask = 0;		/* set by kernel/head.S */
 int x86_capability = 0;		/* set by kernel/head.S */
+int x86_ext_capability = 0;	/* newer CPUs have this */
 int fdiv_bug = 0;		/* set if Pentium(TM) with FP bug */
 int pentium_f00f_bug = 0;	/* set if Pentium(TM) with F00F bug */
 int have_cpuid = 0;             /* set if CPUID instruction works */
+int ext_cpuid = 0;		/* if != 0, highest available CPUID value */
 
-char x86_vendor_id[13] = "unknown";
+char x86_vendor_id[13] = "GenuineIntel";/* default */
 
-unsigned char Cx86_step = 0;
-static const char *Cx86_type[] = {
-	"unknown", "1.3", "1.4", "1.5", "1.6", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2"
+static char *Cx86_step = "unknown";	/* stepping info for Cyrix CPUs */
+
+static unsigned char Cx86_mult = 0;	/* clock multiplier for Cyrix CPUs */
+
+static const char *x86_clkmult[] = {
+	"unknown", "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5", "5.5",
+	"6", "6.5", "7", "7.5", "8"
 	};
 
 char ignore_irq13 = 0;		/* set if exception 16 works */
@@ -89,6 +95,9 @@
  */
 #define PARAM	empty_zero_page
 #define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#ifndef STANDARD_MEMORY_BIOS_CALL
+#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
+#endif
 #ifdef CONFIG_APM
 #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
 #endif
@@ -116,6 +125,9 @@
 	unsigned long * memory_start_p, unsigned long * memory_end_p)
 {
 	unsigned long memory_start, memory_end;
+#ifndef STANDARD_MEMORY_BIOS_CALL
+	unsigned long memory_alt_end;
+#endif
 	char c = ' ', *to = command_line, *from = COMMAND_LINE;
 	int len = 0;
 	static unsigned char smptrap=0;
@@ -134,6 +146,15 @@
 #endif
 	aux_device_present = AUX_DEVICE_INFO;
 	memory_end = (1<<20) + (EXT_MEM_K<<10);
+#ifndef STANDARD_MEMORY_BIOS_CALL
+	memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
+	if (memory_alt_end > memory_end) {
+	    printk("Memory: sized by int13 0e801h\n");
+	    memory_end = memory_alt_end;
+	}
+	else
+	    printk("Memory: sized by int13 088h\n");
+#endif
 	memory_end &= PAGE_MASK;
 #ifdef CONFIG_BLK_DEV_RAM
 	rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
@@ -144,6 +165,17 @@
 	if (memory_end > 16*1024*1024)
 		memory_end = 16*1024*1024;
 #endif
+
+	/*
+	 *	The 1Gig sanity checker.
+	 */
+	 
+	if (memory_end > 980*1024*1024)
+	{
+		printk(KERN_WARNING "Warning only 980Mb will be used.\n");
+		memory_end = 980 * 1024 * 1024;
+	}
+	
 	if (!MOUNT_ROOT_RDONLY)
 		root_mountflags &= ~MS_RDONLY;
 	memory_start = (unsigned long) &_end;
@@ -211,95 +243,107 @@
 	request_region(0xf0,0x10,"npu");
 }
 
-static const char * i486model(unsigned int nr)
-{
-	static const char *model[] = {
-		"0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB",
-		"10","11","12","13","Am5x86-WT","Am5x86-WB"
-	};
-	if (nr < sizeof(model)/sizeof(char *))
-		return model[nr];
-	return NULL;
-}
-
-static const char * i586model(unsigned int nr)
+static const char * IDTmodel(void)
+/* Right now IDT has a single CPU model in production: the C6.
+ * Adjust this when IDT/Centaur comes out with a new CPU model.
+ * Stepping information is correctly reported in x86_mask.
+ */
 {
 	static const char *model[] = {
-		"0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83",
-		"Pentium MMX", NULL, NULL, "Mobile Pentium 75+",
-		"Mobile Pentium MMX"
+		"C6", "C6-3D"
 	};
-	if (nr < sizeof(model)/sizeof(char *))
-		return model[nr];
-	return NULL;
+	return model[0];
 }
 
 static const char * Cx86model(void)
+/* We know our CPU is a Cyrix now (see bugs.h), so we can use the DIR0/DIR1
+ * mechanism to figure out the model, bus clock multiplier and stepping.
+ * For the newest CPUs (GXm and MXi) we use the Extended CPUID function.
+ */
 {
-	unsigned char nr6x86 = 0;
-	static const char *model[] = {
-		"unknown", "6x86", "6x86L", "6x86MX", "MII"
-	};
-	switch (x86) {
-		case 5:
-			nr6x86 = ((x86_capability & (1 << 8)) ? 2 : 1); /* cx8 flag only on 6x86L */
-			break;
-		case 6:
-			nr6x86 = 3;
-			break;
-		default:
-			nr6x86 = 0;
+    unsigned char nr6x86 = 0;
+    unsigned char cx_dir0 = 0;	/* Model and bus clock multiplier */
+    unsigned char cx_dir1 = 0;	/* Stepping info */
+    unsigned int flags;
+    static const char *model[] = {
+	"unknown", "Cx486", "5x86", "MediaGX", "6x86", "6x86L", "6x86MX",
+	"M II"
+    };
+    
+    if (x86_model == -1) {	/* is this an old Cx486 without DIR0/DIR1? */
+	nr6x86 = 1;		/* Cx486 */
+	Cx86_mult = 0;		/* unknown multiplier */
+    }
+    else {
+
+	/* Get DIR0, DIR1 since all other Cyrix CPUs have them */
+    
+	save_flags(flags);
+	cli();
+	cx_dir0 = getCx86(CX86_DIR0);	/* we use the access macros */
+	cx_dir1 = getCx86(CX86_DIR1);	/* defined in processor.h */
+	restore_flags(flags);
+	                                
+	/* Now cook; the recipe is by Channing Corn, from Cyrix.
+	 * We do the same thing for each generation: we work out
+	 * the model, multiplier and stepping.
+	 */
+	 
+	if (cx_dir0 < 0x20) {
+		nr6x86 = 1;				/* Cx486 */
+		Cx86_mult = 0;				/* unknown multiplier */
+		sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
 	}
-
-	/* We must get the stepping number by reading DIR1 */
-	outb(0xff, 0x22); x86_mask=inb(0x23);
-
-	switch (x86_mask) {
-		case 0x03:
-			Cx86_step =  1;	/* 6x86MX Rev 1.3 */
-			break;
-		case 0x04:
-			Cx86_step =  2;	/* 6x86MX Rev 1.4 */
-			break;
-		case 0x05:
-			Cx86_step =  3;	/* 6x86MX Rev 1.5 */
-			break;
-		case 0x06:
-			Cx86_step =  4;	/* 6x86MX Rev 1.6 */
-			break;
-		case 0x14:
-			Cx86_step =  5;	/* 6x86 Rev 2.4 */
-			break;
-		case 0x15:
-			Cx86_step =  6;	/* 6x86 Rev 2.5 */
-			break;
-		case 0x16:
-			Cx86_step =  7;	/* 6x86 Rev 2.6 */
-			break;
-		case 0x17:
-			Cx86_step =  8;	/* 6x86 Rev 2.7 or 3.7 */
-			break;
-		case 0x22:
-			Cx86_step =  9;	/* 6x86L Rev 4.2 */
-			break;
-		default:
-			Cx86_step = 0;
+        
+        if ((cx_dir0 > 0x20) && (cx_dir0 < 0x30)) {
+		nr6x86 = 2;				/* 5x86 */
+		Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3);	/* either 3x or 2x */
+		sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
 	}
-	return model[nr6x86];
-}
-
-static const char * i686model(unsigned int nr)
-{
-	static const char *model[] = {
-		"PPro A-step", "Pentium Pro"
-	};
-	if (nr < sizeof(model)/sizeof(char *))
-		return model[nr];
-	return NULL;
+        
+        if ((cx_dir0 >= 0x30) && (cx_dir0 < 0x38)) {
+		nr6x86 = ((x86_capability & (1 << 8)) ? 5 : 4);	/* 6x86(L) */
+		Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3);	/* either 3x or 2x */
+		sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 3), cx_dir1 & 0x0f);
+	}
+        
+        if ((cx_dir0 >= 0x40) && (cx_dir0 < 0x50)) {
+	    if (x86 == 4) { /* MediaGX */
+		nr6x86 = 3;
+		Cx86_mult = ((cx_dir0 & 0x01) ? 5 : 7);	/* either 3x or 4x */
+        	switch (cx_dir1 >> 4) {
+            		case (0) :
+            		case (1) :
+            			sprintf(Cx86_step, "2.%d", cx_dir1 & 0x0f);
+			break;
+            		case (2) :
+            			sprintf(Cx86_step, "1.%d", cx_dir1 & 0x0f);
+			break;
+                        default  :
+			break;
+                }
+            } /* endif MediaGX */
+            if (x86 == 5) { /* GXm */
+                char GXm_mult[8] = {7,11,7,11,13,15,13,9}; /* 4 to 8 */
+            	ext_cpuid = 0x80000005;	/* available */
+            	Cx86_mult = GXm_mult[cx_dir0 & 0x0f];
+            	sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) - 1, cx_dir1 & 0x0f);
+            } /* endif GXm */
+        }
+        
+        if ((cx_dir0 >= 0x50) && (cx_dir0 < 0x60)) {
+		nr6x86 = ((cx_dir1 > 7) ? 7 : 6);	/* 6x86Mx or M II */
+		Cx86_mult = (cx_dir0 & 0x07) + 2;	/* 2 to 5 in 0.5 steps */
+        	if (((cx_dir1 & 0x0f) > 4) || ((cx_dir1 >> 4) == 2)) cx_dir1 += 0x10;
+        	sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
+	}
+    }
+    x86_mask = 1;	/* we don't use it, but has to be set to something */
+    return model[nr6x86];
 }
 
 struct cpu_model_info {
-	int x86;
+	int cpu_x86;
 	char *model_names[16];
 };
 
@@ -308,10 +352,7 @@
 	  { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4",
 	    "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }},
 	{ 5,
-	  { "K5/SSA5 (PR-75, PR-90, PR-100)", "K5 (PR-120, PR-133)",
-	    "K5 (PR-166)", "K5 (PR-200)", NULL, NULL,
-	    "K6 (166 - 266)", "K6 (166 - 300)", "K6-2 (200 - 450)",
-	    "K6-3D-Plus (200 - 450)", NULL, NULL, NULL, NULL, NULL, NULL }},
+	  { "K5/SSA5 (PR-75, PR-90, PR-100)"}},
 };
 
 static const char * AMDmodel(void)
@@ -319,41 +360,94 @@
 	const char *p=NULL;
 	int i;
 	
-	if (x86_model < 16)
+	if ((x86_model == 0) || (x86 == 4)) {
 		for (i=0; i<sizeof(amd_models)/sizeof(struct cpu_model_info); i++)
-			if (amd_models[i].x86 == x86) {
+			if (amd_models[i].cpu_x86 == x86) {
 				p = amd_models[i].model_names[(int)x86_model];
 				break;
 			}
+	}
+	else ext_cpuid = 0x80000005;		/* available */
+	return p;
+}
+
+static struct cpu_model_info intel_models[] = {
+	{ 4,
+	  { "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL", 
+	    "486 SX/2", NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, 
+	    NULL, NULL, NULL, NULL, NULL }},
+	{ 5,
+	  { "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75+",
+	    "OverDrive PODP5V83", "Pentium MMX", NULL, NULL,
+	    "Mobile Pentium 75+", "Mobile Pentium MMX", NULL, NULL, NULL,
+	    NULL, NULL, NULL, NULL }},
+	{ 6,
+	  { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", 
+	    NULL, "Pentium II (Deschutes)", "Celeron (Mendocino)", NULL, NULL, NULL, NULL, NULL,
+	    NULL, NULL, NULL, NULL }},
+};
+
+static const char * Intelmodel(void)
+{
+	const char *p = "386 SX/DX";	/* default to a 386 */
+	int i;
+	
+	/*
+	 *	Old 486SX has no CPU ID. Set the model to 2 for this
+	 *	case.
+	 */
+	 
+	if( x86==4 && x86_model == 0 && hard_math == 0)
+		x86_model = 2;
+	
+	for (i=0; i<sizeof(intel_models)/sizeof(struct cpu_model_info); i++)
+		if (intel_models[i].cpu_x86 == x86) {
+			p = intel_models[i].model_names[(int)x86_model];
+			break;
+		}
+	
+	
 	return p;
 }
+	
+/* Recent Intel CPUs have an EEPROM and a ROM with CPU information. We'll use
+ * this information in future versions of this code.
+ * AMD and more recently Cyrix have decided to standardize on an extended
+ * cpuid mechanism for their CPUs.
+ */
+ 
+static const char * get_cpu_mkt_name(void)
+{
+        static char mktbuf[48];
+        int dummy;
+	unsigned int *v;
+	v = (unsigned int *) mktbuf;
+	cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);	/* name, flags */
+	cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
+	cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
+	cpuid(0x80000001, &dummy, &dummy, &dummy, &x86_ext_capability);
+	return mktbuf;
+}
 
-static const char * getmodel(int x86, int model)
+static const char * getmodel(void)
+/* Default is Intel. We disregard Nexgen processors. */
 {
         const char *p = NULL;
-        static char nbuf[12];
-	if (strncmp(x86_vendor_id, "Cyrix", 5) == 0)
-		p = Cx86model();
-	else if(strcmp(x86_vendor_id, "AuthenticAMD")==0)
+	if      (strcmp(x86_vendor_id, "AuthenticAMD") == 0)	/* AuthenticAMD */
 		p = AMDmodel();
-	else {
-		switch (x86) {
-			case 4:
-				p = i486model(model);
-				break;
-			case 5:
-				p = i586model(model);
-				break;
-			case 6:
-				p = i686model(model);
-				break;
-		}
-	}
-        if (p)
+	else if (strcmp(x86_vendor_id, "CyrixInstead") == 0)	/* CyrixInstead */
+		p = Cx86model();
+	else if (strcmp(x86_vendor_id, "CentaurHauls") == 0)	/* CentaurHauls */
+		p = IDTmodel();
+	/* This isnt quite right */
+	else if (strcmp(x86_vendor_id, "UMC UMC UMC ") == 0) 	/* UMC */
+		p = Intelmodel();
+	else /* default - this could be anyone */
+		p = Intelmodel();
+	if (ext_cpuid)
+		return get_cpu_mkt_name();
+        else
                 return p;
-
-        sprintf(nbuf, "%d", model);
-        return nbuf;
 }
 
 int get_cpuinfo(char * buffer)
@@ -361,11 +455,17 @@
         int i, len = 0;
         static const char *x86_cap_flags[] = {
                 "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
-                "cx8", "apic", "10", "11", "mtrr", "pge", "mca", "cmov",
+                "cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
                 "16", "17", "18", "19", "20", "21", "22", "mmx",
                 "24", "25", "26", "27", "28", "29", "30", "31"
         };
-        
+	static const char *x86_ext_cap_flags[] = {
+		   "fpu","vme", "de",   "pse", "tsc", "msr",  "6",   "mce",
+		   "cx8",  "9", "10", "syscr",  "12", "pge", "14",  "cmov",
+		"fpcmov", "17", "18",    "19",  "20",  "21", "22",   "mmx",
+		  "emmx", "25", "26",    "27",  "28",  "29", "30", "3dnow"
+	};
+         
 #ifdef __SMP__
         int n;
 
@@ -385,25 +485,30 @@
 
                         len += sprintf(buffer+len,"processor\t: %d\n"
                                        "cpu\t\t: %c86\n"
-                                       "model\t\t: %s\n"
-                                       "vendor_id\t: %s\n",
+                                       "model\t\t: %s",
                                        CPUN,
                                        CD(x86)+'0',
-                                       CD(have_cpuid) ? 
-                                         getmodel(CD(x86), CD(x86_model)) :
-                                         "unknown",
-                                       CD(x86_vendor_id));
+                                       getmodel());
+			len += sprintf(buffer+len,
+                                       "\nvendor_id\t: %s\n",
+                                       x86_vendor_id);
         
-                        if (CD(x86_mask))
-                                if (strncmp(x86_vendor_id, "Cyrix", 5) != 0) {
+                        if (CD(x86_mask) || have_cpuid)
+                                if      ((strncmp(x86_vendor_id, "Au", 2) == 0)
+                                	&& (x86_model >= 6)) {
                                 	len += sprintf(buffer+len,
-                                        	       "stepping\t: %d\n",
-                                             	       CD(x86_mask));
+                                        	       "stepping\t: %c\n",
+                                             	       x86_mask + 'A');
+                                }
+                                else if (strncmp(x86_vendor_id, "Cy", 2) == 0) {
+                                	len += sprintf(buffer+len,
+							"stepping\t: %s, core/bus clock ratio: %sx\n",
+                              				Cx86_step, x86_clkmult[Cx86_mult]);
                                 }
-                                else { 			/* we have a Cyrix */
+                                else { 	
                                 	len += sprintf(buffer+len,
-                                        	       "stepping\t: %s\n",
-                                             	       Cx86_type[Cx86_step]);
+                                        	       "stepping\t: %d\n",
+                                             	       CD(x86_mask));
                                 }
                         else
                                 len += sprintf(buffer+len, 
@@ -431,6 +536,10 @@
                                 if ( CD(x86_capability) & (1 << i) ) {
                                         len += sprintf(buffer+len, " %s",
                                                        x86_cap_flags[i]);
+                                }
+                                else if ( CD(x86_ext_capability) & (1 << i) ) {
+                                        len += sprintf(buffer+len, " %s",
+                                                       x86_ext_cap_flags[i]);
                                 }
                         }
                         len += sprintf(buffer+len,
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.0.35/linux/arch/i386/kernel/smp.c	Mon Jul 13 13:46:25 1998
+++ linux/arch/i386/kernel/smp.c	Sun Nov 15 10:32:46 1998
@@ -35,6 +35,9 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 #include <asm/smp.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
 
 /*
  *	Why isn't this somewhere standard ??
@@ -552,6 +555,14 @@
  	l=apic_read(APIC_SPIV);
  	l|=(1<<8);		/* Enable */
  	apic_write(APIC_SPIV,l);
+
+#ifdef CONFIG_MTRR
+	/*
+	 * checks the MTRR configuration of this application processor
+	 */
+	check_mtrr_config();
+#endif
+
  	sti();
 	/*
 	 *	Get our bogomips.
diff -u --recursive --new-file v2.0.35/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.0.35/linux/arch/i386/kernel/traps.c	Sun Nov 15 10:49:27 1998
+++ linux/arch/i386/kernel/traps.c	Sun Nov 15 10:32:46 1998
@@ -192,9 +192,9 @@
 DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current)
 DO_ERROR(18, SIGSEGV, "reserved", reserved, current)
 
-/* signal_return is directly after ret_from_sys_call in entry.S */
+/* divide_error is after ret_from_sys_call in entry.S */
 asmlinkage void ret_from_sys_call(void)	__asm__("ret_from_sys_call");
-asmlinkage void signal_return(void)	__asm__("signal_return");
+asmlinkage void divide_error(void)	__asm__("divide_error");
 
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
@@ -210,7 +210,7 @@
 	 */
 	if ((regs->cs & 3) != 3) {
 		if (regs->eip >= (unsigned long)ret_from_sys_call &&
-		    regs->eip < (unsigned long)signal_return) {
+		    regs->eip < (unsigned long)divide_error) {
 			static int moancount = 0;
 			if (moancount < 5) {
 				printk(KERN_INFO "Ignoring GPF attempt from program \"%s\" (pid %d).\n",
diff -u --recursive --new-file v2.0.35/linux/arch/i386/lib/delay.S linux/arch/i386/lib/delay.S
--- v2.0.35/linux/arch/i386/lib/delay.S	Tue Sep 16 14:55:07 1997
+++ linux/arch/i386/lib/delay.S	Sun Nov 15 10:32:47 1998
@@ -6,8 +6,12 @@
  */
 
 ENTRY(__do_delay)
-1:	decl %eax
-	jns 1b
+	jmp 1f
+.align 16
+1:	jmp 2f
+.align 16
+2:	decl %eax
+	jns 2b
 	ret
 
 
diff -u --recursive --new-file v2.0.35/linux/arch/i386/math-emu/reg_u_sub.S linux/arch/i386/math-emu/reg_u_sub.S
--- v2.0.35/linux/arch/i386/math-emu/reg_u_sub.S	Thu Oct  5 06:30:43 1995
+++ linux/arch/i386/math-emu/reg_u_sub.S	Sun Nov 15 10:32:47 1998
@@ -66,7 +66,7 @@
 	/* source 2 is always smaller than source 1 */
 	js	L_bugged_1
 
-	testl	$0x80000000,SIGH(%edi)	/* The args are assumed to be be normalized */
+	testl	$0x80000000,SIGH(%edi)	/* The args are assumed to be normalized */
 	je	L_bugged_2
 
 	testl	$0x80000000,SIGH(%esi)
diff -u --recursive --new-file v2.0.35/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.0.35/linux/arch/i386/mm/init.c	Tue Aug 12 13:06:54 1997
+++ linux/arch/i386/mm/init.c	Sun Nov 15 10:32:47 1998
@@ -140,6 +140,17 @@
 		 */
 		if (!smp_scan_config(639*0x400,0x400))	/* Scan the top 1K of base RAM */
 			smp_scan_config(0xF0000,0x10000);	/* Scan the 64K of bios */
+		/*
+		 * If it is an SMP machine we should know now, unless the
+		 * configuration is in an EISA/MCA bus machine with an
+		 * extended bios data area. 
+		 *
+		 * there is a real-mode segmented pointer pointing to the
+		 * 4K EBDA area at 0x40E, calculate and scan it here:
+		 */
+		address = *(unsigned short *)phys_to_virt(0x40E);
+		address<<=4;
+		smp_scan_config(address, 0x1000);
 	}
 	/*
 	 *	If it is an SMP machine we should know now, unless the configuration
diff -u --recursive --new-file v2.0.35/linux/arch/m68k/amiga/cyberfb.c linux/arch/m68k/amiga/cyberfb.c
--- v2.0.35/linux/arch/m68k/amiga/cyberfb.c	Tue May 21 09:51:42 1996
+++ linux/arch/m68k/amiga/cyberfb.c	Sun Nov 15 10:32:47 1998
@@ -29,7 +29,6 @@
 #include <linux/tty.h>
 #include <linux/malloc.h>
 #include <linux/delay.h>
-#include <linux/config.h>
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/irq.h>
diff -u --recursive --new-file v2.0.35/linux/arch/m68k/atari/atasound.c linux/arch/m68k/atari/atasound.c
--- v2.0.35/linux/arch/m68k/atari/atasound.c	Sun May 19 21:54:26 1996
+++ linux/arch/m68k/atari/atasound.c	Sun Nov 15 10:32:47 1998
@@ -17,7 +17,6 @@
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/major.h>
-#include <linux/config.h>
 #include <linux/fcntl.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.0.35/linux/arch/m68k/atari/ksyms.c linux/arch/m68k/atari/ksyms.c
--- v2.0.35/linux/arch/m68k/atari/ksyms.c	Wed May 15 23:05:10 1996
+++ linux/arch/m68k/atari/ksyms.c	Sun Nov 15 10:32:47 1998
@@ -1,4 +1,3 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/ptrace.h>
 #include <asm/traps.h>
diff -u --recursive --new-file v2.0.35/linux/arch/m68k/boot/amiga/bootstrap.c linux/arch/m68k/boot/amiga/bootstrap.c
--- v2.0.35/linux/arch/m68k/boot/amiga/bootstrap.c	Sun May 19 21:54:26 1996
+++ linux/arch/m68k/boot/amiga/bootstrap.c	Sun Nov 15 10:32:48 1998
@@ -1,6 +1,6 @@
 /*
 ** bootstrap.c -- This program loads the Linux/68k kernel into an Amiga
-**                and and launches it.
+**                and launches it.
 **
 ** Copyright 1993,1994 by Hamish Macdonald, Greg Harp
 **
diff -u --recursive --new-file v2.0.35/linux/arch/m68k/kernel/sys_m68k.c linux/arch/m68k/kernel/sys_m68k.c
--- v2.0.35/linux/arch/m68k/kernel/sys_m68k.c	Wed May 15 23:05:10 1996
+++ linux/arch/m68k/kernel/sys_m68k.c	Sun Nov 15 10:32:48 1998
@@ -6,7 +6,6 @@
  * platform.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
diff -u --recursive --new-file v2.0.35/linux/arch/ppc/kernel/mk_defs.c linux/arch/ppc/kernel/mk_defs.c
--- v2.0.35/linux/arch/ppc/kernel/mk_defs.c	Mon Jul  8 01:27:42 1996
+++ linux/arch/ppc/kernel/mk_defs.c	Sun Nov 15 10:32:48 1998
@@ -5,7 +5,6 @@
 #define MK_DEFS
 #include <stdio.h> 
 
-#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/head.h>
diff -u --recursive --new-file v2.0.35/linux/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- v2.0.35/linux/arch/ppc/kernel/pci.c	Mon May 27 02:00:58 1996
+++ linux/arch/ppc/kernel/pci.c	Sun Nov 15 10:32:48 1998
@@ -6,7 +6,6 @@
  * be worked out to handle the differences.
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
diff -u --recursive --new-file v2.0.35/linux/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- v2.0.35/linux/arch/ppc/kernel/time.c	Mon May 27 02:00:58 1996
+++ linux/arch/ppc/kernel/time.c	Sun Nov 15 10:32:49 1998
@@ -26,7 +26,6 @@
 #include <asm/mc146818rtc.h>
 
 #include <linux/timex.h>
-#include <linux/config.h>
 
 extern int isBeBox[];
 
diff -u --recursive --new-file v2.0.35/linux/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c
--- v2.0.35/linux/arch/ppc/mm/fault.c	Mon Jul  8 01:27:43 1996
+++ linux/arch/ppc/mm/fault.c	Sun Nov 15 10:32:50 1998
@@ -5,7 +5,6 @@
  *  Ported to PPC by Gary Thomas
  */
 
-#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/head.h>
diff -u --recursive --new-file v2.0.35/linux/arch/sparc/kernel/ksyms.c linux/arch/sparc/kernel/ksyms.c
--- v2.0.35/linux/arch/sparc/kernel/ksyms.c	Sun Mar  3 22:49:55 1996
+++ linux/arch/sparc/kernel/ksyms.c	Sun Nov 15 10:32:50 1998
@@ -4,7 +4,6 @@
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 
diff -u --recursive --new-file v2.0.35/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c
--- v2.0.35/linux/arch/sparc/kernel/sys_sunos.c	Thu Apr 25 03:22:05 1996
+++ linux/arch/sparc/kernel/sys_sunos.c	Sun Nov 15 10:32:50 1998
@@ -26,7 +26,6 @@
  * to do the inverse mapping.
  */
 
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
diff -u --recursive --new-file v2.0.35/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.0.35/linux/drivers/block/floppy.c	Mon Jul 13 13:46:26 1998
+++ linux/drivers/block/floppy.c	Sun Nov 15 10:32:51 1998
@@ -328,7 +328,7 @@
       0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
 
 {{2,  500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6,  83, 3*HZ, 17, {3,1,2,0,2}, 0,
-      0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+      0, { 2, 5, 6,23,10,20,12, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
 
 {{3,  250, 16, 16, 3000,    1*HZ, 3*HZ,  0, SEL_DLY, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
       0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
@@ -1682,12 +1682,14 @@
 	if(do_print)
 		print_result("unexpected interrupt", inr);
 	if (inr == 0){
+		int max_sensei = 4;
 		do {
 			output_byte(FD_SENSEI);
 			inr = result();
 			if(do_print)
 				print_result("sensei", inr);
-		} while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+			max_sensei--;
+		} while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2 && max_sensei);
 	}
 	if (handler) {
 		if(intr_count >= 2) {
@@ -3989,6 +3991,8 @@
 #endif
 
 	if (floppy_grab_irq_and_dma()){
+		del_timer(&fd_timeout);
+		blk_dev[MAJOR_NR].request_fn = NULL;
 		unregister_blkdev(MAJOR_NR,"fd");
 		return -EBUSY;
 	}
@@ -4042,6 +4046,17 @@
 	initialising=0;
 	if (have_no_fdc) {
 		DPRINT("no floppy controllers found\n");
+		request_tq.routine = (void *)(void *) empty;
+		/*
+		 *	When we return we may be unloaded. This little
+		 *	trick forces the immediate_bh handler to have run
+		 *	before we unload it, lest we cause bad things.
+		 */
+		mark_bh(IMMEDIATE_BH);
+		schedule();
+		if (usage_count)
+			floppy_release_irq_and_dma();
+		blk_dev[MAJOR_NR].request_fn = NULL;
 		unregister_blkdev(MAJOR_NR,"fd");
 	}
 	return have_no_fdc;
diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.0.35/linux/drivers/block/ide-cd.c	Sun Nov 15 10:49:31 1998
+++ linux/drivers/block/ide-cd.c	Sun Nov 15 10:32:51 1998
@@ -6,6 +6,7 @@
  *
  * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
  * Copyright (C) 1996, 1997  Erik Andersen <andersee@debian.org>
+ * Copyright (C) 1998  Jens Axboe and Chris Zwilling
  *
  * May be copied or modified under the terms of the GNU General Public License
  * see linux/COPYING for more information.
@@ -126,6 +127,10 @@
  *                        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
+ *
+ * 3.22  Nov 13, 1998 -- New ide-cd maintainers:
+ *                           Jens Axboe <axboe@image.dk>
+ *                           Chris Zwilling <chris@cloudnet.com>
  *
  * 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.
diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.0.35/linux/drivers/block/ide-floppy.c	Sun Nov 15 10:49:31 1998
+++ linux/drivers/block/ide-floppy.c	Sun Nov 15 10:32:51 1998
@@ -28,6 +28,7 @@
  *                       Issue START cmd only if TEST_UNIT_READY fails
  *                       Add CDROMEJECT ioctl
  *                       Clean up error messages a bit
+ * Ver 0.72  Jul  8 98   Limit max sectors only on IOMEGA ZIP 23.D
  */
 
 #include <linux/config.h>
@@ -1376,7 +1377,8 @@
 	if (gcw.drq_type == 1)
 		set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
 	if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 &&
-	    strcmp(drive->id->fw_rev, "21.D") == 0)
+	    ((strcmp(drive->id->fw_rev, "21.D") == 0) ||
+	     (strcmp(drive->id->fw_rev, "23.D") == 0)))
 		floppy->max_sectors = 64;
 	else
 		floppy->max_sectors = IDEFLOPPY_MAX_SECTORS;
diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.0.35/linux/drivers/block/ide-tape.c	Sun Nov 15 10:49:31 1998
+++ linux/drivers/block/ide-tape.c	Sun Nov 15 10:32:51 1998
@@ -42,7 +42,7 @@
  * flag, can be configured by issuing an ioctl to the block device interface,
  * as any other ide device.
  *
- * Our own ide-tape ioctl's can can be issued to either the block device or
+ * Our own ide-tape ioctl's can be issued to either the block device or
  * the character device interface.
  *
  * Maximal throughput with minimal bus load will usually be achieved in the
@@ -261,7 +261,7 @@
  *		loop which checks if the pipeline is empty, and if it is, we
  *		increase the maximum number of stages as necessary until we
  *		reach the optimum value which just manages to keep the tape
- *		busy with with minimum allocated memory or until we reach
+ *		busy with minimum allocated memory or until we reach
  *		IDETAPE_MAX_PIPELINE_STAGES.
  *
  * Concerning (2):
diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.0.35/linux/drivers/block/ide.c	Sun Nov 15 10:49:31 1998
+++ linux/drivers/block/ide.c	Sun Nov 15 10:32:51 1998
@@ -980,7 +980,7 @@
 #if FANCY_STATUS_DUMPS
 		if (drive->media == ide_disk) {
 			printk(" { ");
-			if (err & BBD_ERR)	printk("BadSector ");
+			if (err & ICRC_ERR)	printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
 			if (err & ECC_ERR)	printk("UncorrectableError ");
 			if (err & ID_ERR)	printk("SectorIdNotFound ");
 			if (err & ABRT_ERR)	printk("DriveStatusError ");
@@ -1908,9 +1908,9 @@
  * This function issues a special IDE device request
  * onto the request queue.
  *
- * If action is ide_wait, then then rq is queued at the end of
- * the request queue, and the function sleeps until it has been
- * processed.  This is for use when invoked from an ioctl handler.
+ * If action is ide_wait, then the rq is queued at the end of the
+ * request queue, and the function sleeps until it has been processed.
+ * This is for use when invoked from an ioctl handler.
  *
  * If action is ide_preempt, then the rq is queued at the head of
  * the request queue, displacing the currently-being-processed
diff -u --recursive --new-file v2.0.35/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.0.35/linux/drivers/block/ide.h	Sun Nov 15 10:49:31 1998
+++ linux/drivers/block/ide.h	Sun Nov 15 10:32:52 1998
@@ -605,9 +605,9 @@
  * This function issues a special IDE device request
  * onto the request queue.
  *
- * If action is ide_wait, then then rq is queued at the end of
- * the request queue, and the function sleeps until it has been
- * processed.  This is for use when invoked from an ioctl handler.
+ * If action is ide_wait, then the rq is queued at the end of the
+ * request queue, and the function sleeps until it has been processed.
+ * This is for use when invoked from an ioctl handler.
  *
  * If action is ide_preempt, then the rq is queued at the head of
  * the request queue, displacing the currently-being-processed
diff -u --recursive --new-file v2.0.35/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v2.0.35/linux/drivers/block/loop.c	Tue Aug 12 13:06:54 1997
+++ linux/drivers/block/loop.c	Sun Nov 15 10:32:52 1998
@@ -365,7 +365,7 @@
 	case LO_CRYPT_NONE:
 		break;
 	case LO_CRYPT_XOR:
-		if (info.lo_encrypt_key_size < 0)
+		if (info.lo_encrypt_key_size <= 0)
 			return -EINVAL;
 		break;
 #ifdef DES_AVAILABLE
diff -u --recursive --new-file v2.0.35/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.0.35/linux/drivers/block/md.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/md.c	Sun Nov 15 10:32:52 1998
@@ -543,7 +543,6 @@
     RO_IOCTLS(inode->i_rdev,arg);
     
     default:
-    printk ("Unknown md_ioctl %d\n", cmd);
     return -EINVAL;
   }
 
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/Makefile linux/drivers/block/paride/Makefile
--- v2.0.35/linux/drivers/block/paride/Makefile	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/Makefile	Sun Nov 15 10:32:52 1998
@@ -12,6 +12,7 @@
 MOD_SUB_DIRS := $(SUB_DIRS)
 ALL_SUB_DIRS := $(SUB_DIRS)
 
+MOD_LIST_NAME := PARIDE_MODULES
 L_TARGET := paride.a
 MX_OBJS  :=
 LX_OBJS  := 
@@ -30,7 +31,7 @@
   LX_OBJS += pd.o
 else
   ifeq ($(CONFIG_PARIDE_PD),m)
-    MX_OBJS += pd.o
+    M_OBJS += pd.o
   endif
 endif
 
@@ -38,7 +39,7 @@
   LX_OBJS += pcd.o
 else
   ifeq ($(CONFIG_PARIDE_PCD),m)
-    MX_OBJS += pcd.o
+    M_OBJS += pcd.o
   endif
 endif
 
@@ -46,7 +47,7 @@
   LX_OBJS += pf.o
 else
   ifeq ($(CONFIG_PARIDE_PF),m)
-    MX_OBJS += pf.o
+    M_OBJS += pf.o
   endif
 endif
 
@@ -54,7 +55,7 @@
   LX_OBJS += pt.o
 else
   ifeq ($(CONFIG_PARIDE_PT),m)
-    MX_OBJS += pt.o
+    M_OBJS += pt.o
   endif
 endif
 
@@ -62,7 +63,7 @@
   LX_OBJS += pg.o
 else
   ifeq ($(CONFIG_PARIDE_PG),m)
-    MX_OBJS += pg.o
+    M_OBJS += pg.o
   endif
 endif
 
@@ -70,7 +71,7 @@
   LX_OBJS += aten.o
 else
   ifeq ($(CONFIG_PARIDE_ATEN),m)
-    MX_OBJS += aten.o
+    M_OBJS += aten.o
   endif
 endif
 
@@ -78,7 +79,7 @@
   LX_OBJS += bpck.o
 else
   ifeq ($(CONFIG_PARIDE_BPCK),m)
-    MX_OBJS += bpck.o
+    M_OBJS += bpck.o
   endif
 endif
 
@@ -86,7 +87,7 @@
   LX_OBJS += comm.o
 else
   ifeq ($(CONFIG_PARIDE_COMM),m)
-    MX_OBJS += comm.o
+    M_OBJS += comm.o
   endif
 endif
 
@@ -94,7 +95,7 @@
   LX_OBJS += dstr.o
 else
   ifeq ($(CONFIG_PARIDE_DSTR),m)
-    MX_OBJS += dstr.o
+    M_OBJS += dstr.o
   endif
 endif
 
@@ -102,7 +103,7 @@
   LX_OBJS += kbic.o
 else
   ifeq ($(CONFIG_PARIDE_KBIC),m)
-    MX_OBJS += kbic.o
+    M_OBJS += kbic.o
   endif
 endif
 
@@ -110,7 +111,7 @@
   LX_OBJS += epat.o
 else
   ifeq ($(CONFIG_PARIDE_EPAT),m)
-    MX_OBJS += epat.o
+    M_OBJS += epat.o
   endif
 endif
 
@@ -118,7 +119,7 @@
   LX_OBJS += epia.o
 else
   ifeq ($(CONFIG_PARIDE_EPIA),m)
-    MX_OBJS += epia.o
+    M_OBJS += epia.o
   endif
 endif
 
@@ -126,7 +127,7 @@
   LX_OBJS += fit2.o
 else
   ifeq ($(CONFIG_PARIDE_FIT2),m)
-    MX_OBJS += fit2.o
+    M_OBJS += fit2.o
   endif
 endif
 
@@ -134,7 +135,7 @@
   LX_OBJS += fit3.o
 else
   ifeq ($(CONFIG_PARIDE_FIT3),m)
-    MX_OBJS += fit3.o
+    M_OBJS += fit3.o
   endif
 endif
 
@@ -142,7 +143,7 @@
   LX_OBJS += frpw.o
 else
   ifeq ($(CONFIG_PARIDE_FRPW),m)
-    MX_OBJS += frpw.o
+    M_OBJS += frpw.o
   endif
 endif
 
@@ -150,7 +151,7 @@
   LX_OBJS += on20.o
 else
   ifeq ($(CONFIG_PARIDE_ON20),m)
-    MX_OBJS += on20.o
+    M_OBJS += on20.o
   endif
 endif
 
@@ -158,7 +159,7 @@
   LX_OBJS += on26.o
 else
   ifeq ($(CONFIG_PARIDE_ON26),m)
-    MX_OBJS += on26.o
+    M_OBJS += on26.o
   endif
 endif
 
@@ -166,7 +167,7 @@
   LX_OBJS += ktti.o
 else
   ifeq ($(CONFIG_PARIDE_KTTI),m)
-    MX_OBJS += ktti.o
+    M_OBJS += ktti.o
   endif
 endif
 
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/bpck.c linux/drivers/block/paride/bpck.c
--- v2.0.35/linux/drivers/block/paride/bpck.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/bpck.c	Sun Nov 15 10:32:52 1998
@@ -10,10 +10,11 @@
 /* Changes:
 
 	1.01	GRG 1998.05.05 init_proto, release_proto, pi->delay 
+	1.02    GRG 1998.08.15 default pi->delay returned to 4
 
 */
 
-#define	BPCK_VERSION	"1.01" 
+#define	BPCK_VERSION	"1.02" 
 
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -450,7 +451,7 @@
 {       MOD_DEC_USE_COUNT;
 }
 
-struct pi_protocol bpck = { "bpck",0,5,2,1,256,
+struct pi_protocol bpck = { "bpck",0,5,2,4,256,
 			  bpck_write_regr,
 			  bpck_read_regr,
 			  bpck_write_block,
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/frpw.c linux/drivers/block/paride/frpw.c
--- v2.0.35/linux/drivers/block/paride/frpw.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/frpw.c	Sun Nov 15 10:32:52 1998
@@ -12,10 +12,11 @@
         1.01    GRG 1998.05.06 init_proto, release_proto
 			       fix chip detect
 			       added EPP-16 and EPP-32
+	1.02    GRG 1998.09.23 added hard reset to initialisation process
 
 */
 
-#define	FRPW_VERSION	"1.01" 
+#define	FRPW_VERSION	"1.02" 
 
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -183,6 +184,9 @@
 /*  returns chip_type:   0 = Xilinx, 1 = ASIC   */
 
 {	int olddelay, a, b;
+
+        w0(0); w2(8); udelay(50); w2(0xc);   /* parallel bus reset */
+        udelay(1500000);
 
 	olddelay = pi->delay;
 	pi->delay = 10;
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/jumbo linux/drivers/block/paride/jumbo
--- v2.0.35/linux/drivers/block/paride/jumbo	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/paride/jumbo	Sun Nov 15 10:32:52 1998
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# This script can be used to build "jumbo" modules that contain the
+# base PARIDE support, one protocol module and one high-level driver.
+#
+echo -n "High level driver [pcd] : "
+read X
+HLD=${X:-pcd}
+#
+echo -n "Protocol module [bpck] : "
+read X
+PROTO=${X:-bpck}
+#
+echo -n "Use MODVERSIONS [y] ? "
+read X
+UMODV=${X:-y}
+#
+echo -n "For SMP kernel [n] ? "
+read X
+USMP=${X:-n}
+#
+echo -n "Support PARPORT [n] ? "
+read X
+UPARP=${X:-n}
+#
+echo
+#
+case $USMP in
+	y* | Y* ) FSMP="-D__SMP__"
+		  ;;
+	*)	  FSMP=""
+		  ;;
+esac
+#
+MODI="-include ../../../include/linux/modversions.h"
+#
+case $UMODV in
+	y* | Y* ) FMODV="-DMODVERSIONS $MODI"
+		  ;;
+	*)	  FMODV=""
+		  ;;
+esac
+#
+case $UPARP in
+	y* | Y* ) FPARP="-DCONFIG_PARPORT"
+		  ;;
+	*)	  FPARP=""
+		  ;;
+esac
+#
+TARG=$HLD-$PROTO.o
+FPROTO=-DCONFIG_PARIDE_`echo "$PROTO" | tr [a-z] [A-Z]`
+FK="-D__KERNEL__ -I ../../../include"
+FLCH=-D_LINUX_CONFIG_H
+#
+echo cc $FK $FSMP $FLCH $FPARP $FPROTO -Wall -O2 -o Jb.o -c paride.c
+cc $FK $FSMP $FLCH $FPARP $FPROTO -Wall -O2 -o Jb.o -c paride.c
+#
+echo cc $FK $FSMP -Wall -O2 -o Jp.o -c $PROTO.c
+cc $FK $FSMP -Wall -O2 -o Jp.o -c $PROTO.c
+#
+echo cc $FK $FSMP $FMODV -DMODULE -DPARIDE_JUMBO -Wall -O2 -o Jd.o -c $HLD.c
+cc $FK $FSMP $FMODV -DMODULE -DPARIDE_JUMBO -Wall -O2 -o Jd.o -c $HLD.c
+#
+echo ld -r -o $TARG Jp.o Jb.o Jd.o
+ld -r -o $TARG Jp.o Jb.o Jd.o
+#
+#
+rm Jp.o Jb.o Jd.o
+#
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/on26.c linux/drivers/block/paride/on26.c
--- v2.0.35/linux/drivers/block/paride/on26.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/on26.c	Sun Nov 15 10:32:52 1998
@@ -10,10 +10,11 @@
 /* Changes:
 
         1.01    GRG 1998.05.06 init_proto, release_proto
+	1.02    GRG 1998.09.23 updates for the -E rev chip
 
 */
 
-#define ON26_VERSION      "1.01"
+#define ON26_VERSION      "1.02"
 
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -91,8 +92,8 @@
         }
 }
 
-#define  CCP(x)  w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
-		 w0(0x87);w0(0x78);w0(x);w2(4);
+#define  CCP(x)  w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+		 w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
 
 static void on26_connect ( PIA *pi )
 
@@ -102,7 +103,6 @@
         pi->saved_r2 = r2();
 
         CCP(0x20);
-	w2(0xcd); w2(0xcc); w0(0xff);
 	x = 8; if (pi->mode) x = 9;
 
 	w0(2); P1; w0(8); P2;
@@ -114,11 +114,62 @@
 {       if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); }
 	              else { w0(4); P1; w0(4); P1; }
 	CCP(0x30);
-        w2(0xcd); w2(0xcc); w0(0xff);
         w0(pi->saved_r0);
         w2(pi->saved_r2);
 } 
 
+static int on26_test_port( PIA *pi)  /* hard reset */
+
+{       int     i, m, d;
+
+        pi->saved_r0 = r0();
+        pi->saved_r2 = r2();
+
+        d = pi->delay;
+        m = pi->mode;
+        pi->delay = 5;
+        pi->mode = 0;
+
+        w2(0xc);
+
+        CCP(0x30); CCP(0); 
+
+        w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);
+        i = ((r1() & 0xf0) << 4); w0(0x87);
+        i |= (r1() & 0xf0); w0(0x78);
+        w0(0x20);w2(4);w2(5);
+        i |= ((r1() & 0xf0) >> 4);
+        w2(4);w0(0xff);
+
+        if (i == 0xb5f) {
+
+            w0(2); P1; w0(0);   P2;
+            w0(3); P1; w0(0);   P2;
+            w0(2); P1; w0(8);   P2; udelay(100);
+            w0(2); P1; w0(0xa); P2; udelay(100);
+            w0(2); P1; w0(8);   P2; udelay(1000);
+            
+            on26_write_regr(pi,0,6,0xa0);
+
+            for (i=0;i<100;i++) {
+                if (!(on26_read_regr(pi,0,7) & 0x80)) break;
+                udelay(100000);
+            }
+
+            w0(4); P1; w0(4); P1;
+        }
+
+        CCP(0x30);
+
+        pi->delay = d;
+        pi->mode = m;
+        w0(pi->saved_r0);
+        w2(pi->saved_r2);
+
+        return 5;
+}
+
+
 static void on26_read_block( PIA *pi, char * buf, int count )
 
 {       int     k, a, b;
@@ -240,7 +291,7 @@
                            on26_read_block,
                            on26_connect,
                            on26_disconnect,
-                           0,
+                           on26_test_port,
                            0,
                            0,
                            on26_log_adapter,
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pcd.c linux/drivers/block/paride/pcd.c
--- v2.0.35/linux/drivers/block/paride/pcd.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/pcd.c	Sun Nov 15 10:32:52 1998
@@ -65,10 +65,11 @@
                         (default "pcd")
 
             verbose     This parameter controls the amount of logging
-                        that is done while the driver probes for
-                        devices.  Set it to 0 for a quiet load, or 1 to
-                        see all the progress messages.  (default 0)
-
+                        that the driver will do.  Set it to 0 for
+                        normal operation, 1 to see autoprobe progress
+                        messages, or 2 to see additional debugging
+                        output.  (default 0)
+  
             nice        This parameter controls the driver's use of
                         idle CPU time, at the expense of some speed.
  
@@ -95,10 +96,13 @@
 			        standard for clearing error status.
 				Use spinlocks. Eliminate sti().
 	1.03    GRG 1998.06.16  Eliminated an Ugh
+	1.04	GRG 1998.08.15  Added extra debugging, improvements to
+				pcd_completion, use HZ in loop timing
+	1.05s   GRG 1998.09.24  Added jumbo support, adjust reset timeout
 
 */
 
-#define	PCD_VERSION	"1.03s"
+#define	PCD_VERSION	"1.05s"
 #define PCD_MAJOR	46
 #define PCD_NAME	"pcd"
 #define PCD_UNITS	4
@@ -180,9 +184,9 @@
 #define PCD_RETRIES	     5
 #define PCD_TMO		   800		/* timeout in jiffies */
 #define PCD_DELAY           50          /* spin delay in uS */
-#define PCD_READY_TMO	    20
+#define PCD_READY_TMO	    20		/* in seconds */
 
-#define PCD_SPIN		(10000/PCD_DELAY)*PCD_TMO
+#define PCD_SPIN	(1000000*PCD_TMO)/(HZ*PCD_DELAY)
 
 #define IDE_ERR		0x01
 #define IDE_DRQ         0x08
@@ -246,6 +250,8 @@
 static int pcd_count;			/* number of blocks still to do */
 static char * pcd_buf;			/* buffer for request in progress */
 
+static int pcd_warned = 0;		/* Have we logged a phase warning ? */
+
 /* kernel glue structures */
 
 static struct file_operations pcd_fops = {
@@ -405,6 +411,12 @@
 
 {	int	err;
 
+#ifdef PARIDE_JUMBO
+       { extern paride_init();
+         paride_init();
+       } 
+#endif
+
 	err = pcd_init();
 
 	return err;
@@ -479,23 +491,51 @@
 
 static int pcd_completion( int unit, char * buf,  char * fun )
 
-{	int r, s, n;
+{	int r, d, p, n, k, j;
 
-	r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion");
+	r = -1; k = 0; j = 0;
 
-	if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) { 
-	        n = (((RR(0,4)+256*RR(0,5))+3)&0xfffc);
-	        pi_read_block(PI,buf,n);
+	if (!pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,
+						fun,"completion")) {
+	    r = 0;
+	    while (RR(0,7)&IDE_DRQ) {
+	        d = (RR(0,4)+256*RR(0,5));
+	        n = ((d+3)&0xfffc);
+	        p = RR(0,2)&3;
+
+	        if ((p == 2) && (n > 0) && (j == 0)) {
+		    pi_read_block(PI,buf,n);
+	            if (verbose > 1) 
+			printk("%s: %s: Read %d bytes\n",PCD.name,fun,n);
+		    r = 0; j++;
+	        } else {
+		    if (verbose > 1) 
+		        printk("%s: %s: Unexpected phase %d, d=%d, k=%d\n",
+					PCD.name,fun,p,d,k);
+		    if ((verbose < 2) && !pcd_warned) {
+	               	pcd_warned = 1;
+			printk("%s: WARNING: ATAPI phase errors\n",PCD.name);
+			}
+		    udelay(1000);
+	        } 
+		if (k++ > PCD_TMO) {
+			printk("%s: Stuck DRQ\n",PCD.name);
+			break;
+		}
+	        if (pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,
+				fun,"completion")) { 
+			r = -1;
+			break;
+		}
+	    }
 	}
-
-	s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done");
-
+	
 	pi_disconnect(PI); 
 
-	return (r?r:s);
+	return r;
 }
 
-static void pcd_req_sense( int unit, int quiet )
+static void pcd_req_sense( int unit, char *fun )
 
 {	char	rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 };
 	char	buf[16];
@@ -507,8 +547,8 @@
 
 	PCD.last_sense = -1;
 	if (!r) {
-            if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
-	                       PCD.name,buf[2]&0xf,buf[12],buf[13]);
+            if (fun) printk("%s: %s: Sense key: %x, ASC: %x, ASQ: %x\n",
+	                       PCD.name,fun,buf[2]&0xf,buf[12],buf[13]);
 	    PCD.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
                                           | ((buf[13]&0xff)<<16) ;
         } 
@@ -521,12 +561,12 @@
 	r = pcd_command(unit,cmd,dlen,fun);
 	udelay(1000);
 	if (!r) r = pcd_completion(unit,buf,fun);
-	if (r) pcd_req_sense(unit,!fun);
+	if (r) pcd_req_sense(unit,fun);
 	
 	return r;
 }
 
-#define DBMSG(msg)	NULL
+#define DBMSG(msg)	((verbose>1)?(msg):NULL)
 
 static void pcd_lock(int unit)
 
@@ -558,7 +598,7 @@
 	pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject");
 }
 
-#define PCD_RESET_TMO	30		/* in tenths of a second */
+#define PCD_RESET_TMO	100		/* in tenths of a second */
 
 static void pcd_sleep( int cs )
 
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pd.c linux/drivers/block/paride/pd.c
--- v2.0.35/linux/drivers/block/paride/pd.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/pd.c	Sun Nov 15 10:32:52 1998
@@ -113,10 +113,12 @@
 	1.02    GRG 1998.05.06  SMP spinlock changes, 
 				Added slave support
 	1.03    GRG 1998.06.16  Eliminate an Ugh.
+	1.04s   GRG 1998.09.24  Added jumbo support
+				Use HZ in loop timings, extra debugging
 
 */
 
-#define PD_VERSION      "1.03s"
+#define PD_VERSION      "1.04s"
 #define PD_MAJOR	45
 #define PD_NAME		"pd"
 #define PD_UNITS	4
@@ -218,7 +220,7 @@
 #define PD_TMO          800             /* interrupt timeout in jiffies */
 #define PD_SPIN_DEL     50              /* spin delay in micro-seconds  */
 
-#define PD_SPIN         (10000/PD_SPIN_DEL)*PD_TMO  
+#define PD_SPIN         (1000000*PD_TMO)/(HZ*PD_SPIN_DEL)
 
 #define STAT_ERR        0x00001
 #define STAT_INDEX      0x00002
@@ -595,6 +597,12 @@
 
 {       int     err, unit;
 
+#ifdef PARIDE_JUMBO
+       { extern paride_init();
+         paride_init();
+       } 
+#endif
+
         err = pd_init();
         if (err) return err;
 
@@ -653,7 +661,7 @@
 	udelay(250);
 }
 
-#define DBMSG(msg)	NULL
+#define DBMSG(msg)    ((verbose>1)?(msg):NULL)
 
 static int pd_wait_for( int unit, int w, char * msg )    /* polled wait */
 
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pf.c linux/drivers/block/paride/pf.c
--- v2.0.35/linux/drivers/block/paride/pf.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/pf.c	Sun Nov 15 10:32:52 1998
@@ -111,10 +111,12 @@
 				Small change in pf_completion to round
 				up transfer size.
 	1.02    GRG 1998.06.16  Eliminated an Ugh
+	1.03s   GRG 1998.09.24  Added jumbo support
+				Use HZ in loop timings, extra debugging
 
 */
 
-#define PF_VERSION      "1.02s"
+#define PF_VERSION      "1.03s"
 #define PF_MAJOR	47
 #define PF_NAME		"pf"
 #define PF_UNITS	4
@@ -211,7 +213,7 @@
 #define PF_TMO          800             /* interrupt timeout in jiffies */
 #define PF_SPIN_DEL     50              /* spin delay in micro-seconds  */
 
-#define PF_SPIN         (10000/PF_SPIN_DEL)*PF_TMO  
+#define PF_SPIN         (1000000*PF_TMO)/(HZ*PF_SPIN_DEL)
 
 #define STAT_ERR        0x00001
 #define STAT_INDEX      0x00002
@@ -495,6 +497,12 @@
 
 {       int     err;
 
+#ifdef PARIDE_JUMBO
+       { extern paride_init();
+         paride_init();
+       } 
+#endif
+
         err = pf_init();
 
         return err;
@@ -616,7 +624,7 @@
         return r;
 }
 
-#define DBMSG(msg)      NULL
+#define DBMSG(msg)      ((verbose>1)?(msg):NULL)
 
 static void pf_lock(int unit, int func)
 
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pg.c linux/drivers/block/paride/pg.c
--- v2.0.35/linux/drivers/block/paride/pg.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/pg.c	Sun Nov 15 10:32:52 1998
@@ -38,10 +38,10 @@
 	To use this device, you must have the following device 
 	special files defined:
 
-		/dev/pg0 b 97 0
-		/dev/pg1 b 97 1
-		/dev/pg2 b 97 2
-		/dev/pg3 b 97 3
+		/dev/pg0 c 97 0
+		/dev/pg1 c 97 1
+		/dev/pg2 c 97 2
+		/dev/pg3 c 97 3
 
 	(You'll need to change the 97 to something else if you use
 	the 'major' parameter to install the driver on a different
@@ -121,9 +121,11 @@
 /* Changes:
 
 	1.01	GRG 1998.06.16	Bug fixes
+	1.02    GRG 1998.09.24  Added jumbo support
+
 */
 
-#define PG_VERSION      "1.01s"
+#define PG_VERSION      "1.02s"
 #define PG_MAJOR	97
 #define PG_NAME		"pg"
 #define PG_UNITS	4
@@ -318,6 +320,12 @@
 int     init_module(void)
 
 {       int     err;
+
+#ifdef PARIDE_JUMBO
+       { extern paride_init();
+         paride_init();
+       } 
+#endif
 
         err = pg_init();
 
diff -u --recursive --new-file v2.0.35/linux/drivers/block/paride/pt.c linux/drivers/block/paride/pt.c
--- v2.0.35/linux/drivers/block/paride/pt.c	Sun Nov 15 10:49:32 1998
+++ linux/drivers/block/paride/pt.c	Sun Nov 15 10:32:52 1998
@@ -74,10 +74,11 @@
 			(default "pt").
 
             verbose     This parameter controls the amount of logging
-                        that is done while the driver probes for
-                        devices.  Set it to 0 for a quiet load, or 1 to
-                        see all the progress messages.  (default 0)
-
+                        that the driver will do.  Set it to 0 for
+                        normal operation, 1 to see autoprobe progress
+                        messages, or 2 to see additional debugging
+                        output.  (default 0)
+ 
         If this driver is built into the kernel, you can use 
         the following command line parameters, with the same values
         as the corresponding module parameters listed above:
@@ -99,10 +100,13 @@
 				for clearing error status.
 				Eliminate sti();
 	1.02    GRG 1998.06.16  Eliminate an Ugh.
-
+	1.03    GRG 1998.08.15  Adjusted PT_TMO, use HZ in loop timing,
+				extra debugging
+	1.04    GRG 1998.09.24  Repair minor coding error, added jumbo support
+	
 */
 
-#define PT_VERSION      "1.02s"
+#define PT_VERSION      "1.04s"
 #define PT_MAJOR	96
 #define PT_NAME		"pt"
 #define PT_UNITS	4
@@ -168,13 +172,13 @@
 #include "paride.h"
 
 #define PT_MAX_RETRIES  5
-#define PT_TMO          800             /* interrupt timeout in jiffies */
+#define PT_TMO          3000            /* interrupt timeout in jiffies */
 #define PT_SPIN_DEL     50              /* spin delay in micro-seconds  */
-#define PT_RESET_TMO    30		/* 3 seconds */
+#define PT_RESET_TMO    30		/* 30 seconds */
 #define PT_READY_TMO	60		/* 60 seconds */
 #define PT_REWIND_TMO	1200		/* 20 minutes */
 
-#define PT_SPIN         (10000/PT_SPIN_DEL)*PT_TMO  
+#define PT_SPIN         ((1000000/(HZ*PT_SPIN_DEL))*PT_TMO)  
 
 #define STAT_ERR        0x00001
 #define STAT_INDEX      0x00002
@@ -317,6 +321,12 @@
 
 {       int     err;
 
+#ifdef PARIDE_JUMBO
+       { extern paride_init();
+         paride_init();
+       } 
+#endif
+
         err = pt_init();
 
         return err;
@@ -498,7 +508,7 @@
         pt_media_access_cmd(unit,PT_TMO,wm_cmd,"write filemark");
 }
 
-#define DBMSG(msg)      NULL
+#define DBMSG(msg)      ((verbose>1)?(msg):NULL)
 
 static int pt_reset( int unit )
 
@@ -573,8 +583,8 @@
 	char	*ms[2] = {"master","slave"};
 	char	mf[10], id[18];
 	char    id_cmd[12] = { ATAPI_IDENTIFY,0,0,0,36,0,0,0,0,0,0,0};
-        char    ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,128,0,0,0,0,0,0,0};
-	char    ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,128,0,0,0};
+        char    ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,36,0,0,0,0,0,0,0};
+	char    ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,36,0,0,0};
 	char	buf[36];
 
         s = pt_atapi(unit,id_cmd,36,buf,"identify");
diff -u --recursive --new-file v2.0.35/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c
--- v2.0.35/linux/drivers/cdrom/sonycd535.c	Sun Nov 15 10:49:33 1998
+++ linux/drivers/cdrom/sonycd535.c	Sun Nov 15 10:32:53 1998
@@ -156,6 +156,8 @@
 # define CDU535_MESSAGE_NAME	"Sony CDU-535"
 #endif
 
+#define CDU535_BLOCK_SIZE	2048
+
 #ifndef MAX_SPINUP_RETRY
 # define MAX_SPINUP_RETRY		3	/* 1 is sufficient for most drives... */
 #endif
@@ -594,8 +596,6 @@
  *  it returns one of the standard error returns.
  ***************************************************************************/
 
-static int sonycd535_block_size = 2048;
-
 static int
 seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
 					   Byte **buff, int buf_size)
@@ -607,7 +607,7 @@
 	Byte *data_buff;
 	int  sector_count = 0;
 
-	if (buf_size < sonycd535_block_size * n_blocks)
+	if (buf_size < CDU535_BLOCK_SIZE * n_blocks)
 		return NO_ROOM;
 
 	set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
@@ -632,7 +632,7 @@
 			if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
 				/* data is ready, read it */
 				data_buff = buff[sector_count++];
-				for (i = 0; i < sonycd535_block_size; i++)
+				for (i = 0; i < CDU535_BLOCK_SIZE; i++)
 					*data_buff++ = inb(data_reg);	/* unrolling this loop does not seem to help */
 				break;			/* exit the timeout loop */
 			}
@@ -645,7 +645,7 @@
 	/* read all the data, now read the status */
 	if ((i = read_exec_status(status)) != 0)
 		return i;
-	return sonycd535_block_size * sector_count;
+	return CDU535_BLOCK_SIZE * sector_count;
 }	/* seek_and_read_N_blocks() */
 
 /****************************************************************************
@@ -782,7 +782,7 @@
  * The OS calls this to perform a read or write operation to the drive.
  * Write obviously fail.  Reads to a read ahead of sony_buffer_size
  * bytes to help speed operations.  This especially helps since the OS
- * uses 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
+ * may use 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
  * data access on a CD is done sequentially, this saves a lot of operations.
  */
 static void
@@ -879,7 +879,7 @@
 						 * seek_and_read_N_blocks for the various cases.
 						 */
 						int readStatus = seek_and_read_N_blocks(params, read_size,
-									status, sony_buffer, (read_size * 2048));
+									status, sony_buffer, (read_size * CDU535_BLOCK_SIZE));
 						if (0 <= readStatus)	/* Good data; common case, placed first */
 							break;
 						if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) {
@@ -1485,6 +1485,8 @@
 	NULL						/* revalidate */
 };
 
+static int sonycd535_block_size = CDU535_BLOCK_SIZE;
+
 /*
  * Initialize the driver.
  */
@@ -1583,7 +1585,7 @@
 			if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
 				/* set the drive mode successful, we are set! */
 				sony_buffer_size = SONY535_BUFFER_SIZE;
-				sony_buffer_sectors = sony_buffer_size / 2048;
+				sony_buffer_sectors = sony_buffer_size / CDU535_BLOCK_SIZE;
 
 				printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
 					   drive_config.vendor_id,
@@ -1621,7 +1623,8 @@
 					return -ENOMEM;
 				}
 				for (i = 0; i < sony_buffer_sectors; i++) {
-					sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL);
+					sony_buffer[i] =
+								(Byte *)kmalloc(CDU535_BLOCK_SIZE, GFP_KERNEL);
 					if (sony_buffer[i] == NULL) {
 						while (--i>=0)
 							kfree(sony_buffer[i]);
@@ -1684,7 +1687,7 @@
 
 	release_region(sony535_cd_base_io, 4);
 	for (i = 0; i < sony_buffer_sectors; i++)
-		kfree_s(sony_buffer[i], 2048);
+		kfree_s(sony_buffer[i], CDU535_BLOCK_SIZE);
 	kfree_s(sony_buffer, 4 * sony_buffer_sectors);
 	kfree_s(last_sony_subcode, sizeof *last_sony_subcode);
 	kfree_s(sony_toc, sizeof *sony_toc);
diff -u --recursive --new-file v2.0.35/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.0.35/linux/drivers/char/Config.in	Mon Jul 13 13:46:26 1998
+++ linux/drivers/char/Config.in	Sun Nov 15 10:32:53 1998
@@ -7,6 +7,9 @@
 tristate 'Standard/generic serial support' CONFIG_SERIAL
 bool 'Digiboard PC/Xx Support' CONFIG_DIGI
 tristate 'Cyclades async mux support' CONFIG_CYCLADES
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+	dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m
+fi
 bool 'Stallion multiport serial support' CONFIG_STALDRV
 if [ "$CONFIG_STALDRV" = "y" ]; then
   tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION
diff -u --recursive --new-file v2.0.35/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.0.35/linux/drivers/char/Makefile	Sun Nov 15 10:49:33 1998
+++ linux/drivers/char/Makefile	Sun Nov 15 10:32:53 1998
@@ -86,6 +86,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_ISI),y)
+L_OBJS += isicom.o
+else
+  ifeq ($(CONFIG_ISI),m)
+  M_OBJS += isicom.o
+  endif
+endif
+
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
 M = y
 L_OBJS += atixlmouse.o
diff -u --recursive --new-file v2.0.35/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.0.35/linux/drivers/char/apm_bios.c	Sun Nov 15 10:49:33 1998
+++ linux/drivers/char/apm_bios.c	Sun Nov 15 10:32:53 1998
@@ -40,6 +40,8 @@
  *         is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth
  *         <jtoth@princeton.edu>); improve interaction between
  *         screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4
+ *    1.2a: Fix OOPs on power off with no APM BIOS
+ *         Jan Echternach <echter@informatik.uni-rostock.de>
  *
  * Reference:
  *
@@ -444,16 +446,24 @@
 	return APM_SUCCESS;
 }
 
-int apm_set_power_state(u_short state)
+static int apm_set_power_state(u_short state)
 {
 	u_short	error;
-
+	
 	APM_SET_POWER_STATE(state, error);
 	if (error & 0xff)
 		return (error >> 8);
 	return APM_SUCCESS;
 }
 
+#ifdef CONFIG_APM_POWER_OFF
+void apm_power_off(void)
+{
+	if (apm_enabled)
+		(void) apm_set_power_state(APM_STATE_OFF);
+}
+#endif
+
 #ifdef CONFIG_APM_DISPLAY_BLANK
 /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */
 static int apm_set_display_power_state(u_short state)
@@ -1100,6 +1110,16 @@
 }
 #endif
 
+static int apm_disabled = 0;
+
+void apm_setup(char *str, int *ints)
+{
+	if(strcmp(str,"off")==0)
+		apm_disabled=1;
+	if(strcmp(str,"on")==0)
+		apm_disabled=0;
+}
+
 void apm_bios_init(void)
 {
 	unsigned short	bx;
@@ -1108,6 +1128,12 @@
 	unsigned short	error;
 	char *		power_stat;
 	char *		bat_stat;
+	
+	if (apm_disabled == 1)
+	{
+		printk("APM disabled.\n");
+		return;
+	}
 
 	if (apm_bios_info.version == 0) {
 		printk("APM BIOS not found.\n");
diff -u --recursive --new-file v2.0.35/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.0.35/linux/drivers/char/cyclades.c	Mon Oct  7 22:03:17 1996
+++ linux/drivers/char/cyclades.c	Sun Nov 15 10:32:53 1998
@@ -1,13 +1,17 @@
+#define BLOCKMOVE
+#define	Z_WAKE
 static char rcsid[] =
-"$Revision: 1.36.3.9 $$Date: 1996/10/07 19:47:13 $";
+"$Revision: 2.1.1.10 $$Date: 1998/11/12 16:08:23 $";
+
 /*
  *  linux/drivers/char/cyclades.c
  *
  * This file contains the driver for the Cyclades Cyclom-Y multiport
  * serial boards.
  *
- * Maintained by Marcio Saito (marcio@cyclades.com) and
- * Randolph Bentson (bentson@grieg.seaslug.org)
+ * Maintained by Ivan Passos (ivan@cyclades.com),
+ * Marcio Saito (marcio@cyclades.com) and 
+ * Randolph Bentson (bentson@grieg.seaslug.org).
  *
  * For Technical support and installation problems, please send e-mail
  * to support@cyclades.com.
@@ -22,11 +26,239 @@
  * This module exports the following rs232 io functions:
  *   int cy_init(void);
  *   int cy_open(struct tty_struct *tty, struct file *filp);
+ * and the following functions for modularization.
+ *   int init_module(void);
+ *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
- * Revision 1.36.3.9  1996/10/07 19:47:13  bentson
- * add MOD_DEC_USE_COUNT in one return from cy_close (as
- * noted by Jon Lewis <jlewis@INORGANIC5.FDT.NET>)
+ * Revision: 2.1.1.10  1998/11/12 16:08:23 ivan
+ * cy_close function now resets (correctly) the tty->closing flag;
+ * JIFFIES_DIFF macro fixed.
+ *
+ * Revision 2.1.1.9  1998/09/02 14:47:01 ivan
+ * Fixed bug in cy_close function, which was not informing HW of
+ * which port should have the reception disabled before doing so;
+ * fixed Cyclom-8YoP hardware detection bug.
+ * 
+ * Revision 2.1.1.8  1998/08/20 17:04:54 ivan
+ * Fixed bug in cy_close function, which causes malfunction
+ * of one of the first 4 ports when a higher port is closed
+ * (Cyclom-Y only).
+ *
+ * Revision 2.1.1.7  1998/08/10 17:01:45 ivan
+ * Fixed Cyclom-4Yo hardware detection bug.
+ *
+ * Revision 2.1.1.6  1998/08/04 12:19:36 ivan
+ * cyy_interrupt changed once more to avoid occurence of kernel
+ * oopses during PPP operation.
+ *
+ * Revision 2.1.1.5  1998/08/03 16:55:59 ivan
+ * /proc/cyclades implementation (only for monolythic driver)
+ * with great collaboration of Marc Lewis <marc@blarg.net>;
+ * cyy_interrupt was changed to avoid occurence of kernel oopses
+ * during PPP operation.
+ *
+ * Revision 2.1.1.4  1998/06/01 18:01:19 ivan
+ * data loss prevention revisited (cy_wait_until_sent created);
+ * MOD_COUNT bug (which caused the usage decrementing not to work 
+ * properly) fixed.
+ *
+ * Revision 2.1.1.3b  1998/05/14 10:14:00 ivan
+ * temporary workaround for losing SIGHUPs in PPP connections
+ * (Cyclom-Y only).
+ *
+ * Revision 2.1.1.3a  1998/03/16 18:01:12 ivan
+ * cleaned up the data loss fix;
+ * fixed XON/XOFF handling once more (Cyclades-Z);
+ * general revision in the driver routines;
+ * introduction of a mechanism to prevent data loss with slow 
+ * printers, by forcing a delay before closing the port.
+ *
+ * Revision 2.1.1.2a  1998/02/17 16:50:00 ivan
+ * fixed detection/handling of new CD1400 in Ye boards;
+ * fixed XON/XOFF handling (Cyclades-Z);
+ * fixed data loss caused by a premature port close;
+ * introduction of a flag that holds the CD1400 version ID per port
+ * (used by the CYGETCD1400VER new ioctl).
+ *
+ * Revision 2.1.1.1a  1997/12/03 17:31:19 ivan
+ * Code review for the module cleanup routine;
+ * fixed RTS and DTR status report for new CD1400's in get_modem_info;
+ * includes anonymous changes regarding signal_pending.
+ * 
+ * Revision 2.1  1997/11/01 17:42:41 ivan
+ * Changes in the driver to support Alpha systems (except 8Zo V_1);
+ * BREAK fix for the Cyclades-Z boards;
+ * driver inactivity control by FW implemented;
+ * introduction of flag that allows driver to take advantage of 
+ * a special CD1400 feature related to HW flow control;
+ * added support for the CD1400  rev. J (Cyclom-Y boards);
+ * introduction of ioctls to:
+ *  - control the rtsdtr_inv flag (Cyclom-Y);
+ *  - control the rflow flag (Cyclom-Y);
+ *  - adjust the polling interval (Cyclades-Z);
+ *
+ * Revision 1.36.4.33  1997/06/27 19:00:00  ivan
+ * Fixes related to kernel version conditional 
+ * compilation.
+ *  
+ * Revision 1.36.4.32  1997/06/14 19:30:00  ivan
+ * Compatibility issues between kernels 2.0.x and 
+ * 2.1.x (mainly related to clear_bit function).
+ *  
+ * Revision 1.36.4.31  1997/06/03 15:30:00  ivan
+ * Changes to define the memory window according to the 
+ * board type.
+ *  
+ * Revision 1.36.4.30  1997/05/16 15:30:00  daniel
+ * Changes to suport new cycladesZ boards.
+ *
+ * Revision 1.36.4.29  1997/05/12 11:30:00  daniel
+ * Merge of Bentson's and Daniel's version 1.36.4.28.
+ * Corrects bug in cy_detect_pci: check if there are more
+ * ports than the number of static structs allocated.
+ * Warning message during initialization if this driver is
+ * used with the new generation of cycladesZ boards.  Those
+ * will be supported only in next release of the driver.
+ * Corrects bug in cy_detect_pci and cy_detect_isa that
+ * returned wrong number of VALID boards, when a cyclomY
+ * was found with no serial modules connected.
+ * Changes to use current (2.1.x) kernel subroutine names
+ * and created macros for compilation with 2.0.x kernel,
+ * instead of the other way around.
+ *
+ * Revision 1.36.4.28  1997/05/?? ??:00:00  bentson
+ * Change queue_task_irq_off to queue_task_irq.
+ * The inline function queue_task_irq_off (tqueue.h)
+ * was removed from latest releases of 2.1.x kernel.
+ * Use of macro __initfunc to mark the initialization
+ * routines, so memory can be reused.
+ * Also incorporate implementation of critical region
+ * in function cleanup_module() created by anonymous
+ * linuxer.
+ *
+ * Revision 1.36.4.28  1997/04/25 16:00:00  daniel
+ * Change to support new firmware that solves DCD problem:
+ * application could fail to receive SIGHUP signal when DCD
+ * varying too fast.
+ *
+ * Revision 1.36.4.27  1997/03/26 10:30:00  daniel
+ * Changed for suport linux versions 2.1.X.
+ * Backward compatible with linux versions 2.0.X.
+ * Corrected illegal use of filler field in
+ * CH_CTRL struct.
+ * Deleted some debug messages.
+ *
+ * Revision 1.36.4.26  1997/02/27 12:00:00  daniel
+ * Included check for NULL tty pointer in cyz_poll.
+ *
+ * Revision 1.36.4.25  1997/02/26 16:28:30  bentson
+ * Bill Foster at Blarg! Online services noticed that
+ * some of the switch elements of -Z modem control
+ * lacked a closing "break;"
+ *
+ * Revision 1.36.4.24  1997/02/24 11:00:00  daniel
+ * Changed low water threshold for buffer xmit_buf
+ *
+ * Revision 1.36.4.23  1996/12/02 21:50:16  bentson
+ * Marcio provided fix to modem status fetch for -Z
+ *
+ * Revision 1.36.4.22  1996/10/28 22:41:17  bentson
+ * improve mapping of -Z control page (thanks to Steve
+ * Price <stevep@fa.tdktca.com> for help on this)
+ *
+ * Revision 1.36.4.21  1996/09/10 17:00:10  bentson
+ * shift from CPU-bound to memcopy in cyz_polling operation
+ *
+ * Revision 1.36.4.20  1996/09/09 18:30:32  Bentson
+ * Added support to set and report higher speeds.
+ *
+ * Revision 1.36.4.19c  1996/08/09 10:00:00  Marcio Saito
+ * Some fixes in the HW flow control for the BETA release.
+ * Don't try to register the IRQ.
+ *
+ * Revision 1.36.4.19  1996/08/08 16:23:18  Bentson
+ * make sure "cyc" appears in all kernel messages; all soft interrupts
+ * handled by same routine; recognize out-of-band reception; comment
+ * out some diagnostic messages; leave RTS/CTS flow control to hardware;
+ * fix race condition in -Z buffer management; only -Y needs to explictly
+ * flush chars; tidy up some startup messages;
+ *
+ * Revision 1.36.4.18  1996/07/25 18:57:31  bentson
+ * shift MOD_INC_USE_COUNT location to match
+ * serial.c; purge some diagnostic messages;
+ *
+ * Revision 1.36.4.17  1996/07/25 18:01:08  bentson
+ * enable modem status messages and fetch & process them; note
+ * time of last activity type for each port; set_line_char now
+ * supports more than line 0 and treats 0 baud correctly;
+ * get_modem_info senses rs_status;
+ *
+ * Revision 1.36.4.16  1996/07/20 08:43:15  bentson
+ * barely works--now's time to turn on
+ * more features 'til it breaks
+ *
+ * Revision 1.36.4.15  1996/07/19 22:30:06  bentson
+ * check more -Z board status; shorten boot message
+ *
+ * Revision 1.36.4.14  1996/07/19 22:20:37  bentson
+ * fix reference to ch_ctrl in startup; verify return
+ * values from cyz_issue_cmd and cyz_update_channel;
+ * more stuff to get modem control correct;
+ *
+ * Revision 1.36.4.13  1996/07/11 19:53:33  bentson
+ * more -Z stuff folded in; re-order changes to put -Z stuff
+ * after -Y stuff (to make changes clearer)
+ *
+ * Revision 1.36.4.12  1996/07/11 15:40:55  bentson
+ * Add code to poll Cyclades-Z.  Add code to get & set RS-232 control.
+ * Add code to send break.  Clear firmware ID word at startup (so
+ * that other code won't talk to inactive board).
+ *
+ * Revision 1.36.4.11  1996/07/09 05:28:29  bentson
+ * add code for -Z in set_line_char
+ *
+ * Revision 1.36.4.10  1996/07/08 19:28:37  bentson
+ * fold more -Z stuff (or in some cases, error messages)
+ * into driver; add text to "don't know what to do" messages.
+ *
+ * Revision 1.36.4.9  1996/07/08 18:38:38  bentson
+ * moved compile-time flags near top of file; cosmetic changes
+ * to narrow text (to allow 2-up printing); changed many declarations
+ * to "static" to limit external symbols; shuffled code order to
+ * coalesce -Y and -Z specific code, also to put internal functions
+ * in order of tty_driver structure; added code to recognize -Z
+ * ports (and for moment, do nothing or report error); add cy_startup
+ * to parse boot command line for extra base addresses for ISA probes;
+ *
+ * Revision 1.36.4.8  1996/06/25 17:40:19  bentson
+ * reorder some code, fix types of some vars (int vs. long),
+ * add cy_setup to support user declared ISA addresses
+ *
+ * Revision 1.36.4.7  1996/06/21 23:06:18  bentson
+ * dump ioctl based firmware load (it's now a user level
+ * program); ensure uninitialzed ports cannot be used
+ *
+ * Revision 1.36.4.6  1996/06/20 23:17:19  bentson
+ * rename vars and restructure some code
+ *
+ * Revision 1.36.4.5  1996/06/14 15:09:44  bentson
+ * get right status back after boot load
+ *
+ * Revision 1.36.4.4  1996/06/13 19:51:44  bentson
+ * successfully loads firmware
+ *
+ * Revision 1.36.4.3  1996/06/13 06:08:33  bentson
+ * add more of the code for the boot/load ioctls
+ *
+ * Revision 1.36.4.2  1996/06/11 21:00:51  bentson
+ * start to add Z functionality--starting with ioctl
+ * for loading firmware
+ *
+ * Revision 1.36.4.1  1996/06/10 18:03:02  bentson
+ * added code to recognize Z/PCI card at initialization; report
+ * presence, but card is not initialized (because firmware needs
+ * to be loaded)
  *
  * Revision 1.36.3.8  1996/06/07 16:29:00  bentson
  * starting minor number at zero; added missing verify_area
@@ -261,6 +493,63 @@
  *
  */
 
+/* If you need to install more boards than NR_CARDS, change the constant
+   in the definition below. No other change is necessary to support up to
+   eight boards. Beyond that you'll have to extend cy_isa_addresses. */
+
+#define NR_CARDS        4
+
+/*
+   If the total number of ports is larger than NR_PORTS, change this
+   constant in the definition below. No other change is necessary to
+   support more boards/ports. */
+
+#define NR_PORTS        128
+
+#define ZE_V1_NPORTS	64
+#define ZO_V1	0
+#define ZO_V2	1
+#define ZE_V1	2
+
+#define	SERIAL_PARANOIA_CHECK
+#undef	CY_DEBUG_OPEN
+#undef	CY_DEBUG_THROTTLE
+#undef	CY_DEBUG_OTHER
+#undef	CY_DEBUG_IO
+#undef	CY_DEBUG_COUNT
+#undef	CY_DEBUG_DTR
+#undef	CY_DEBUG_WAIT_UNTIL_SENT
+#undef	CY_16Y_HACK
+#undef	CY_ENABLE_MONITORING
+#undef	CY_PCI_DEBUG
+
+#if 0
+#define PAUSE __asm__("nop");
+#else
+#define PAUSE ;
+#endif
+
+#define cy_min(a,b) (((a)<(b))?(a):(b))
+
+#if 0
+/******** 
+ * For the next two macros, it is assumed that the buffer size is a 
+ * power of 2 
+ ********/
+
+#define CHARS_IN_BUF(buf_ctrl) \
+    ((cy_readl(&buf_ctrl->rx_put) - \
+      cy_readl(&buf_ctrl->rx_get) + \
+      cy_readl(&buf_ctrl->rx_bufsize)) & \
+		  (cy_readl(&buf_ctrl->rx_bufsize) - 1))
+
+#define SPACE_IN_BUF(buf_ctrl) \
+    ((cy_readl(&buf_ctrl->tx_get) - \
+      cy_readl(&buf_ctrl->tx_put) + \
+      cy_readl(&buf_ctrl->tx_bufsize) - 1) & \
+		  (cy_readl(&buf_ctrl->tx_bufsize) - 1))
+#endif
+
 #include <linux/module.h>
 
 #include <linux/errno.h>
@@ -289,30 +578,44 @@
 #include <linux/bios32.h>
 #include <linux/pci.h>
 
-#define small_delay(x) for(j=0;j<x;j++)k++;
+#include <linux/version.h>
 
+#ifdef CONFIG_PROC_FS
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#endif
 
-#define SERIAL_PARANOIA_CHECK
-#undef  SERIAL_DEBUG_OPEN
-#undef  SERIAL_DEBUG_THROTTLE
-#undef  SERIAL_DEBUG_OTHER
-#undef  SERIAL_DEBUG_IO
-#undef  SERIAL_DEBUG_COUNT
-#undef  SERIAL_DEBUG_DTR
-#undef  CYCLOM_16Y_HACK
-#undef  CYCLOM_ENABLE_MONITORING
+#define __initfunc(__arginit)	__arginit
+#define copy_from_user		memcpy_fromfs
+#define copy_to_user		memcpy_tofs
+#define cy_get_user		get_fs_long
+#define cy_put_user		put_fs_long
+#define ioremap			vremap
 
 #ifndef MIN
-#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#define MIN(a,b)        ((a) < (b) ? (a) : (b))
 #endif
 
-#define WAKEUP_CHARS 256
+#define IS_CYC_Z(card) ((card).num_chips == -1)
+
+#define Z_FPGA_CHECK(card) \
+    ((cy_readl(&((struct RUNTIME_9060 *) \
+		 ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0)
+
+#define ISZLOADED(card)	(((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \
+			((card).ctl_addr))->mail_box_0)) || \
+			Z_FPGA_CHECK(card)) && \
+			(ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \
+			((card).base_addr+ID_ADDRESS))->signature)))
+
+#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256)
 
 #define STD_COM_FLAGS (0)
 
 #define SERIAL_TYPE_NORMAL  1
 #define SERIAL_TYPE_CALLOUT 2
 
+#define	JIFFIES_DIFF(n, j)	((j) - (n))
 
 DECLARE_TASK_QUEUE(tq_cyclades);
 
@@ -321,75 +624,41 @@
 static volatile int cy_irq_triggered;
 static volatile int cy_triggered;
 static int cy_wild_int_mask;
-static unsigned char *intr_base_addr;
+static volatile ucchar *intr_base_addr;
 
-
-/* This is the address lockup table. The driver will probe for Cyclom-Y/ISA
-   boards at all addresses in here. If you want the driver to probe addresses
-   in a different address, add it to this table.
-   If the driver is probing some other board and causing problems, remove the
-   address from this table.  */
+/* This is the address lookup table. The driver will probe for
+   Cyclom-Y/ISA boards at all addresses in here. If you want the
+   driver to probe addresses at a different address, add it to
+   this table.  If the driver is probing some other board and
+   causing problems, remove the offending address from this table.
+   The cy_setup function extracts additional addresses from the
+   boot options line.  The form is "cyclades=address,address..."
+*/
 
 static unsigned char *cy_isa_addresses[] = {
-	(unsigned char *) 0xD0000,
-	(unsigned char *) 0xD2000,
-	(unsigned char *) 0xD4000,
-	(unsigned char *) 0xD6000,
-	(unsigned char *) 0xD8000,
-	(unsigned char *) 0xDA000,
-	(unsigned char *) 0xDC000,
-	(unsigned char *) 0xDE000,
+        (unsigned char *) 0xD0000,
+        (unsigned char *) 0xD2000,
+        (unsigned char *) 0xD4000,
+        (unsigned char *) 0xD6000,
+        (unsigned char *) 0xD8000,
+        (unsigned char *) 0xDA000,
+        (unsigned char *) 0xDC000,
+        (unsigned char *) 0xDE000,
+        0,0,0,0,0,0,0,0
 };
-#define	NR_ISA_ADDRESSES	(sizeof(cy_isa_addresses)/sizeof(unsigned char *))
+#define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*))
 
 /* This is the per-card data structure containing address, irq, number of
-   channels, etc. This driver supports a maximum of NR_CARDS cards. If
-   you need to install more boards, change this constant in the definition
-   below. No other change is necessary to support more boards. */
-
-#define NR_CARDS	4
-
+   channels, etc. This driver supports a maximum of NR_CARDS cards.
+*/
 static struct cyclades_card cy_card[NR_CARDS];
 
 /* This is the per-channel data structure containing pointers, flags
-   and variables for the port. This driver supports a maximum of NR_PORTS.
-   If the total number of ports is larger than NR_PORTS, change this
-   constant in the definition below. No other change is necessary to
-   support more boards/ports. */
-
-#define NR_PORTS        64
-
+ and variables for the port. This driver supports a maximum of NR_PORTS.
+*/
 static struct cyclades_port cy_port[NR_PORTS];
 
-/*  The Cyclom-Ye has placed the sequential chips in non-sequential
- *  address order.  This look-up table overcomes that problem.
- */
-static int cy_chip_offset [] =
-    { 0x0000,
-      0x0400,
-      0x0800,
-      0x0C00,
-      0x0200,
-      0x0600,
-      0x0A00,
-      0x0E00
-    };
-
-/* PCI related definitions */
-
-static unsigned short	cy_pci_nboard = 0;
-static unsigned short	cy_isa_nboard = 0;
-static unsigned short	cy_nboard = 0;
-static unsigned short	cy_pci_dev_id[] = {
-				PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */
-				PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */
-				0			/* end of table */
-			};
-
-int			cy_detect_isa(void);
-int			cy_detect_pci(void);
-
-static int		cy_next_channel = 0;	/* next minor available */
+static int cy_next_channel = 0; /* next minor available */
 
 static int serial_refcount;
 
@@ -400,17 +669,18 @@
 /* This is the per-irq data structure,
    it maps an irq to the corresponding card */
 
-struct cyclades_card	*IRQ_cards[16];
+static struct cyclades_card     *IRQ_cards[16];
 
 
 /*
  * tmp_buf is used as a temporary buffer by serial_write.  We need to
- * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * lock it in case the copy_from_user blocks while swapping in a page,
  * and some other program tries to do a serial write at the same time.
  * Since the lock will only come under contention when the system is
  * swapping and available memory is low, it makes sense to share one
  * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
+ * memory if large numbers of serial ports are open.  This buffer is
+ * allocated when the first cy_open occurs.
  */
 static unsigned char *tmp_buf = 0;
 static struct semaphore tmp_buf_sem = MUTEX;
@@ -419,83 +689,173 @@
  * This is used to look up the divisor speeds and the timeouts
  * We're normally limited to 15 distinct baud rates.  The extra
  * are accessed via settings in info->flags.
- *         0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
- *        10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
- *                                                  HI            VHI
+ *      0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
+ *     10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+ *                                               HI            VHI
+ *     20
  */
 static int baud_table[] = {
-           0,    50,    75,   110,   134,   150,   200,   300,   600,  1200,
-        1800,  2400,  4800,  9600, 19200, 38400, 57600, 76800,115200,150000,
-        0};
-
-static char baud_co[] = {  /* 25 MHz clock option table */
-        /* value =>    00    01   02    03    04 */
-        /* divide by    8    32   128   512  2048 */
-        0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,  0x03,  0x02,
-        0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};
-
-static char baud_bpr[] = {  /* 25 MHz baud rate period table */
-        0x00,  0xf5,  0xa3,  0x6f,  0x5c,  0x51,  0xf5,  0xa3,  0x51,  0xa3,
-        0x6d,  0x51,  0xa3,  0x51,  0xa3,  0x51,  0x36,  0x29,  0x1b,  0x15};
+       0,    50,    75,   110,   134,   150,   200,   300,   600,  1200,
+    1800,  2400,  4800,  9600, 19200, 38400, 57600, 76800,115200,150000,
+  230400,     0};
+
+static char baud_co_25[] = {  /* 25 MHz clock option table */
+    /* value =>    00    01   02    03    04 */
+    /* divide by    8    32   128   512  2048 */
+    0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,  0x03,  0x02,
+    0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};
+
+static char baud_bpr_25[] = {  /* 25 MHz baud rate period table */
+    0x00,  0xf5,  0xa3,  0x6f,  0x5c,  0x51,  0xf5,  0xa3,  0x51,  0xa3,
+    0x6d,  0x51,  0xa3,  0x51,  0xa3,  0x51,  0x36,  0x29,  0x1b,  0x15};
+
+static char baud_co_60[] = {  /* 60 MHz clock option table (CD1400 J) */
+    /* value =>    00    01   02    03    04 */
+    /* divide by    8    32   128   512  2048 */
+    0x00,  0x00,  0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,
+    0x03,  0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00};
+
+static char baud_bpr_60[] = {  /* 60 MHz baud rate period table (CD1400 J) */
+    0x00,  0x82,  0x21,  0xff,  0xdb,  0xc3,  0x92,  0x62,  0xc3,  0x62,
+    0x41,  0xc3,  0x62,  0xc3,  0x62,  0xc3,  0x82,  0x62,  0x41,  0x32,
+    0x21};
 
 static char baud_cor3[] = {  /* receive threshold */
-        0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
-        0x0a,  0x0a,  0x0a,  0x09,  0x09,  0x08,  0x08,  0x08,  0x08,  0x07};
+    0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
+    0x0a,  0x0a,  0x0a,  0x09,  0x09,  0x08,  0x08,  0x08,  0x08,  0x07,
+    0x07};
+
+/*
+ * The Cyclades driver implements HW flow control as any serial driver.
+ * The cyclades_port structure member rflow and the vector rflow_thr 
+ * allows us to take advantage of a special feature in the CD1400 to avoid 
+ * data loss even when the system interrupt latency is too high. These flags 
+ * are to be used only with very special applications. Setting these flags 
+ * requires the use of a special cable (DTR and RTS reversed). In the new 
+ * CD1400-based boards (rev. 6.00 or later), there is no need for special 
+ * cables.
+ */
+
+static char rflow_thr[] = {  /* rflow threshold */
+    0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00,
+    0x00,  0x00,  0x00,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
+    0x0a};
 
+/*  The Cyclom-Ye has placed the sequential chips in non-sequential
+ *  address order.  This look-up table overcomes that problem.
+ */
+static int cy_chip_offset [] =
+    { 0x0000,
+      0x0400,
+      0x0800,
+      0x0C00,
+      0x0200,
+      0x0600,
+      0x0A00,
+      0x0E00
+    };
 
+/* PCI related definitions */
 
-static void shutdown(struct cyclades_port *);
-static int startup (struct cyclades_port *);
-static void cy_throttle(struct tty_struct *);
-static void cy_unthrottle(struct tty_struct *);
-static void config_setup(struct cyclades_port *);
-#ifdef CYCLOM_SHOW_STATUS
+static unsigned short   cy_pci_nboard = 0;
+static unsigned short   cy_isa_nboard = 0;
+static unsigned short   cy_nboard = 0;
+static unsigned short   cy_pci_dev_id[] = {
+			    PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */
+			    PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */
+			    PCI_DEVICE_ID_CYCLOM_Z_Lo,/* PCI below 1Mb */
+			    PCI_DEVICE_ID_CYCLOM_Z_Hi,/* PCI above 1Mb */
+			    0                       /* end of table */
+                        };
+
+
+static void cy_start(struct tty_struct *);
+static void set_line_char(struct cyclades_port *);
+static void cy_probe(int, void *, struct pt_regs *);
+static void cyz_poll(unsigned long);
+#ifdef CY_SHOW_STATUS
 static void show_status(int);
 #endif
 
+#if defined(CONFIG_PROC_FS) && !defined(MODULE)
+static int cyclades_get_proc_info(char *, char **, off_t, int, int);
+static struct proc_dir_entry cyclades_proc_entry = {
+	0, 
+	8, 
+	"cyclades", 
+	S_IFREG | S_IRUGO, 
+	1, 
+	0, 
+	0, 
+	0, 
+	0, 
+	cyclades_get_proc_info
+};
+#endif
+
+/* The Cyclades-Z polling cycle is defined by this variable */
+static long cyz_polling_cycle = CZ_DEF_POLL;
+
+static int cyz_timeron = 0;
+static struct timer_list
+cyz_timerlist = {
+    NULL, NULL, 0, 0, cyz_poll
+};
+
+/**************************************************
+error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+copy_to_user  (to, from, count);
+***************************************************************
+error = verify_area(VERIFY_READ,  (void *) arg, sizeof(unsigned long *));
+copy_from_user(to, from, count);
+**************************************************/
+
 
 static inline int
 serial_paranoia_check(struct cyclades_port *info,
-			kdev_t device, const char *routine)
+                        kdev_t device, const char *routine)
 {
 #ifdef SERIAL_PARANOIA_CHECK
     static const char *badmagic =
-	"Warning: bad magic number for serial struct (%s) in %s\n";
+        "cyc Warning: bad magic number for serial struct (%s) in %s\n";
     static const char *badinfo =
-	"Warning: null cyclades_port for (%s) in %s\n";
+        "cyc Warning: null cyclades_port for (%s) in %s\n";
     static const char *badrange =
-	"Warning: cyclades_port out of range for (%s) in %s\n";
+        "cyc Warning: cyclades_port out of range for (%s) in %s\n";
 
     if (!info) {
-	printk(badinfo, kdevname(device), routine);
-	return 1;
+        printk(badinfo, kdevname(device), routine);
+        return 1;
     }
 
     if( (long)info < (long)(&cy_port[0])
     || (long)(&cy_port[NR_PORTS]) < (long)info ){
-	printk(badrange, kdevname(device), routine);
-	return 1;
+        printk(badrange, kdevname(device), routine);
+        return 1;
     }
 
     if (info->magic != CYCLADES_MAGIC) {
-	printk(badmagic, kdevname(device), routine);
-	return 1;
+        printk(badmagic, kdevname(device), routine);
+        return 1;
     }
 #endif
-	return 0;
+        return 0;
 } /* serial_paranoia_check */
 
+
 /* The following diagnostic routines allow the driver to spew
    information on the screen, even (especially!) during interrupts.
  */
-void
+static void
 SP(char *data){
   unsigned long flags;
     save_flags(flags); cli();
         console_print(data);
     restore_flags(flags);
-}
-void
+}/* SP */
+
+static void
 CP(char data){
   unsigned long flags;
   char scrn[2];
@@ -506,111 +866,17 @@
     restore_flags(flags);
 }/* CP */
 
-void CP1(int data) { (data<10)?  CP(data+'0'): CP(data+'A'-10); }/* CP1 */
-void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */
-void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */
-void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */
-
-/* This routine waits up to 1000 micro-seconds for the previous
-   command to the Cirrus chip to complete and then issues the
-   new command.  An error is returned if the previous command
-   didn't finish within the time limit.
- */
-u_short
-write_cy_cmd(u_char *base_addr, u_char cmd, int index)
-{
-  unsigned long flags;
-  volatile int  i;
-
-    save_flags(flags); cli();
-	/* Check to see that the previous command has completed */
-	for(i = 0 ; i < 100 ; i++){
-	    if (base_addr[CyCCR<<index] == 0){
-		break;
-	    }
-	    udelay(10L);
-	}
-	/* if the CCR never cleared, the previous command
-	    didn't finish within the "reasonable time" */
-	if ( i == 100 ) {
-	    restore_flags(flags);
-	    return (-1);
-	}
-
-	/* Issue the new command */
-	base_addr[CyCCR<<index] = cmd;
-    restore_flags(flags);
-    return(0);
-} /* write_cy_cmd */
-
-
-/* cy_start and cy_stop provide software output flow control as a
-   function of XON/XOFF, software CTS, and other such stuff. */
-
-static void
-cy_stop(struct tty_struct *tty)
-{
-  struct cyclades_card *cinfo;
-  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
-  unsigned char *base_addr;
-  int chip,channel,index;
-  unsigned long flags;
-
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_stop ttyC%d\n", info->line); /* */
-#endif
-
-    if (serial_paranoia_check(info, tty->device, "cy_stop"))
-	return;
-	
-    cinfo = &cy_card[info->card];
-    index = cinfo->bus_index;
-    channel = info->line - cinfo->first_line;
-    chip = channel>>2;
-    channel &= 0x03;
-    base_addr = (unsigned char*)
-                   (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index));
-
-    save_flags(flags); cli();
-        base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); /* index channel */
-        base_addr[CySRER<<index] &= ~CyTxMpty;
-    restore_flags(flags);
-
-    return;
-} /* cy_stop */
-
-static void
-cy_start(struct tty_struct *tty)
-{
-  struct cyclades_card *cinfo;
-  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
-  unsigned char *base_addr;
-  int chip,channel,index;
-  unsigned long flags;
-
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_start ttyC%d\n", info->line); /* */
+static void CP4(int data)
+    { (data<10)?  CP(data+'0'): CP(data+'A'-10); }/* CP4 */
+static void CP8(int data)
+    { CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */
+#if 0
+static void CP16(int data)
+    { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */
+static void CP32(long data)
+    { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */
 #endif
 
-    if (serial_paranoia_check(info, tty->device, "cy_start"))
-	return;
-	
-    cinfo = &cy_card[info->card];
-    index = cinfo->bus_index;
-    channel = info->line - cinfo->first_line;
-    chip = channel>>2;
-    channel &= 0x03;
-    base_addr = (unsigned char*)
-                   (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index));
-
-    save_flags(flags); cli();
-        base_addr[CyCAR<<index] = (u_char)(channel & 0x0003);
-        base_addr[CySRER<<index] |= CyTxMpty;
-    restore_flags(flags);
-
-    return;
-} /* cy_start */
-
 
 /*
  * This routine is used by the interrupt handler to schedule
@@ -622,304 +888,601 @@
 cy_sched_event(struct cyclades_port *info, int event)
 {
     info->event |= 1 << event; /* remember what kind of event and who */
-    queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */
+    queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */
     mark_bh(CYCLADES_BH);                       /* then trigger event */
 } /* cy_sched_event */
 
 
-static int probe_ready;
-
 /*
- * This interrupt routine is used
- * while we are probing for submarines.
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using cy_sched_event(), and they get done here.
+ *
+ * This is done through one level of indirection--the task queue.
+ * When a hardware interrupt service routine wants service by the
+ * driver's bottom half, it enqueues the appropriate tq_struct (one
+ * per port) to the tq_cyclades work queue and sets a request flag
+ * via mark_bh for processing that queue.  When the time is right,
+ * do_cyclades_bh is called (because of the mark_bh) and it requests
+ * that the work queue be processed.
+ *
+ * Although this may seem unwieldy, it gives the system a way to
+ * pass an argument (in this case the pointer to the cyclades_port
+ * structure) to the bottom half of the driver.  Previous kernels
+ * had to poll every port to see if that port needed servicing.
  */
 static void
-cy_probe(int irq, void *dev_id, struct pt_regs *regs)
+do_cyclades_bh(void)
 {
-  int save_xir, save_car;
-  int index = 0;	/* probing interrupts is only for ISA */
+    run_task_queue(&tq_cyclades);
+} /* do_cyclades_bh */
 
-    if (!probe_ready) {
-	*(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+static void
+do_softint(void *private_)
+{
+  struct cyclades_port *info = (struct cyclades_port *) private_;
+  struct tty_struct    *tty;
+
+    tty = info->tty;
+    if (!tty)
         return;
+
+    if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
+        tty_hangup(info->tty);
+        wake_up_interruptible(&info->open_wait);
+        info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+                             ASYNC_CALLOUT_ACTIVE);
+    }
+    if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
+        wake_up_interruptible(&info->open_wait);
+    }
+    if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+        if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+        && tty->ldisc.write_wakeup){
+            (tty->ldisc.write_wakeup)(tty);
+        }
+        wake_up_interruptible(&tty->write_wait);
     }
+#ifdef Z_WAKE
+    if (clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) {
+        wake_up_interruptible(&info->shutdown_wait);
+    }
+#endif
+} /* do_softint */
 
-    cy_irq_triggered = irq;
-    cy_triggered |= 1 << irq;
 
-	if(intr_base_addr[CySVRR<<index] != 0) {
-	    save_xir = (u_char) intr_base_addr[CyTIR<<index];
-	    save_car = intr_base_addr[CyCAR<<index];
-	    if ((save_xir & 0x3) != 0){
-		SP("channel ");
-		CP2(save_xir);
-		SP(" requesting unexpected interrupt\n");
-	    }
-	    intr_base_addr[CyCAR<<index] = (save_xir & 0x3);
-	    intr_base_addr[CySRER<<index] &= ~CyTxMpty;
-	    intr_base_addr[CyTIR<<index] = (save_xir & 0x3f);
-	    intr_base_addr[CyCAR<<index] = (save_car);
-	}
-	*(intr_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */
-    return;
-} /* cy_probe */
+/***********************************************************/
+/********* Start of block of Cyclom-Y specific code ********/
 
-/* The real interrupt service routine is called
-   whenever the card wants its hand held--chars
-   received, out buffer empty, modem change, etc.
+/* This routine waits up to 1000 micro-seconds for the previous
+   command to the Cirrus chip to complete and then issues the
+   new command.  An error is returned if the previous command
+   didn't finish within the time limit.
  */
-static void
-cy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static int
+cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index)
 {
-  struct tty_struct *tty;
-  int status;
-  struct cyclades_card *cinfo;
-  struct cyclades_port *info;
-  volatile unsigned char *base_addr, *card_base_addr;
-  int chip;
-  int save_xir, channel, save_car;
-  char data;
-  int char_count;
-  int outch;
-  int i,j,index;
-  int too_many;
-  int had_work;
-  int mdm_change;
-  int mdm_status;
+  unsigned long flags;
+  volatile int  i;
 
-    if((cinfo = IRQ_cards[irq]) == 0){
-        return; /* spurious interrupt */
-    }
+    save_flags(flags); cli();
+        /* Check to see that the previous command has completed */
+        for(i = 0 ; i < 100 ; i++){
+            if (cy_readb(base_addr+(CyCCR<<index)) == 0){
+                break;
+            }
+            udelay(10L);
+        }
+        /* if the CCR never cleared, the previous command
+            didn't finish within the "reasonable time" */
+        if ( i == 100 ) {
+            restore_flags(flags);
+            return (-1);
+        }
 
-    card_base_addr = (unsigned char *)cinfo->base_addr;
-    index = cinfo->bus_index;
+        /* Issue the new command */
+        cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);
+    restore_flags(flags);
+    return(0);
+} /* cyy_issue_cmd */
 
-    /* This loop checks all chips in the card.  Make a note whenever
-       _any_ chip had some work to do, as this is considered an
-       indication that there will be more to do.  Only when no chip
-       has any work does this outermost loop exit.
-     */
-    do{
-        had_work = 0;
-        for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {
-	    base_addr = (unsigned char *)
-	                   (cinfo->base_addr + (cy_chip_offset[chip]<<index));
-            too_many = 0;
-            while ( (status = base_addr[CySVRR<<index]) != 0x00) {
-                had_work++;
-                /* The purpose of the following test is to ensure that
-                   no chip can monopolize the driver.  This forces the
+static int probe_ready;
+
+/*
+ * Grab all interrupts in preparation for doing an automatic irq
+ * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
+ * mask of irq's which were grabbed and should therefore be freed
+ * using free_all_interrupts().
+ */
+static int
+grab_all_interrupts(int dontgrab)
+{
+  int irq_lines = 0;
+  int i, mask;
+    
+    for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+        if (!(mask & dontgrab)
+        && !request_irq(i, cy_probe,
+	             SA_INTERRUPT, "serial probe", NULL)) {
+            irq_lines |= mask;
+        }
+    }
+    return irq_lines;
+} /* grab_all_interrupts */
+
+/*
+ * Release all interrupts grabbed by grab_all_interrupts
+ */
+static void
+free_all_interrupts(int irq_lines)
+{
+  int i;
+    
+    for (i = 0; i < 16; i++) {
+        if (irq_lines & (1 << i)) {
+            free_irq(i,NULL);
+	}
+    }
+} /* free_all_interrupts */
+
+/*
+ * This routine returns a bitfield of "wild interrupts".  Basically,
+ * any unclaimed interrupts which is flapping around.
+ */
+static int
+check_wild_interrupts(void)
+{
+  int   i, mask;
+  int   wild_interrupts = 0;
+  int   irq_lines;
+  unsigned long timeout;
+  unsigned long flags;
+        
+    /*Turn on interrupts (they may be off) */
+    save_flags(flags); sti();
+
+        irq_lines = grab_all_interrupts(0);
+       
+        /*
+         * Delay for 0.1 seconds -- we use a busy loop since this may 
+         * occur during the bootup sequence
+         */
+        timeout = jiffies+(HZ/10);
+        while (timeout >= jiffies)
+            ;
+        
+        cy_triggered = 0;       /* Reset after letting things settle */
+
+        timeout = jiffies+(HZ/10);
+        while (timeout >= jiffies)
+                ;
+        
+        for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+            if ((cy_triggered & (1 << i)) &&
+                (irq_lines & (1 << i))) {
+                    wild_interrupts |= mask;
+            }
+        }
+        free_all_interrupts(irq_lines);
+    restore_flags(flags);
+    return wild_interrupts;
+} /* check_wild_interrupts */
+
+/*
+ * This routine is called by do_auto_irq(); it attempts to determine
+ * which interrupt a serial port is configured to use.  It is not
+ * fool-proof, but it works a large part of the time.
+ */
+static int
+get_auto_irq(volatile ucchar *address)
+{
+  unsigned long   	timeout;
+  volatile ucchar 	*base_addr;
+  int           	index;
+  unsigned long		flags;
+
+    index = 0;  /* IRQ probing is only for ISA */
+    base_addr = address;
+    intr_base_addr = address;
+        
+    /*
+     * Enable interrupts and see who answers
+     */
+    cy_irq_triggered = 0;
+    save_flags(flags); cli();
+        cy_writeb((u_long)base_addr+(CyCAR<<index), 0);
+        cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
+        cy_writeb((u_long)base_addr+(CySRER<<index), 
+             cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
+        probe_ready = 1;
+    restore_flags(flags);
+    
+    timeout = jiffies+(HZ/50);
+    while (timeout >= jiffies) {
+        if (cy_irq_triggered)
+            break;
+    }
+    probe_ready = 0;
+    return(cy_irq_triggered);
+} /* get_auto_irq */
+
+/*
+ * Calls get_auto_irq() multiple times, to make sure we don't get
+ * faked out by random interrupts
+ */
+static int
+do_auto_irq(volatile ucchar *address)
+{
+  int                   irq_lines = 0;
+  int                   irq_try_1 = 0, irq_try_2 = 0;
+  int                   retries;
+  unsigned long		flags;
+
+    /* Turn on interrupts (they may be off) */
+    save_flags(flags); sti();
+
+        probe_ready = 0;
+
+        cy_wild_int_mask = check_wild_interrupts();
+
+        irq_lines = grab_all_interrupts(cy_wild_int_mask);
+        
+        for (retries = 0; retries < 5; retries++) {
+            if (!irq_try_1)
+                irq_try_1 = get_auto_irq(address);
+            if (!irq_try_2)
+                irq_try_2 = get_auto_irq(address);
+            if (irq_try_1 && irq_try_2) {
+                if (irq_try_1 == irq_try_2)
+                    break;
+                irq_try_1 = irq_try_2 = 0;
+            }
+        }
+    restore_flags(flags);
+    free_all_interrupts(irq_lines);
+    return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+} /* do_auto_irq */
+
+
+/*
+ * This interrupt routine is used
+ * while we are probing for submarines.
+ */
+static void
+cy_probe(int irq, void *dev_id, struct pt_regs *regs)
+{
+  int save_xir, save_car;
+  int index = 0;        /* probing interrupts is only for ISA */
+
+    if (!probe_ready) {
+        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
+        return;
+    }
+
+    cy_irq_triggered = irq;
+    cy_triggered |= 1 << irq;
+
+        if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) {
+            save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index));
+            save_car = cy_readb(intr_base_addr+(CyCAR<<index));
+            if ((save_xir & 0x3) != 0){
+                SP("channel ");
+                CP8(save_xir);
+                SP(" requesting unexpected interrupt\n");
+            }
+            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3));
+            cy_writeb((u_long)intr_base_addr+(CySRER<<index),
+                cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty);
+            cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f));
+            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car));
+        }
+        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
+	                          /* Cy_ClrIntr is 0x1800 */
+    return;
+} /* cy_probe */
+
+/* The real interrupt service routine is called
+   whenever the card wants its hand held--chars
+   received, out buffer empty, modem change, etc.
+ */
+static void
+cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct tty_struct *tty;
+  int status;
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info;
+  volatile unsigned char *base_addr, *card_base_addr;
+  int chip;
+  int save_xir, channel, save_car;
+  char data;
+  volatile int char_count;
+  int outch;
+  int i,j,index;
+  int too_many;
+  int had_work;
+  int mdm_change;
+  int mdm_status;
+
+    if((cinfo = IRQ_cards[irq]) == 0){
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: spurious interrupt %d\n\r", irq);
+#endif
+        return; /* spurious interrupt */
+    }
+
+    card_base_addr = (unsigned char *)cinfo->base_addr;
+    index = cinfo->bus_index;
+
+
+    /* This loop checks all chips in the card.  Make a note whenever
+       _any_ chip had some work to do, as this is considered an
+       indication that there will be more to do.  Only when no chip
+       has any work does this outermost loop exit.
+     */
+    do{
+        had_work = 0;
+        for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {
+            base_addr = (unsigned char *)
+		       (cinfo->base_addr + (cy_chip_offset[chip]<<index));
+            too_many = 0;
+            while ( (status = cy_readb(base_addr+(CySVRR<<index))) != 0x00) {
+                had_work++;
+                /* The purpose of the following test is to ensure that
+                   no chip can monopolize the driver.  This forces the
                    chips to be checked in a round-robin fashion (after
                    draining each of a bunch (1000) of characters).
-		 */
+                 */
                 if(1000<too_many++){
                     break;
                 }
-                if (status & CySRReceive) {      /* reception interrupt */
-                    /* determine the channel and change to that context */
-                    save_xir = (u_char) base_addr[CyRIR<<index];
+                if (status & CySRReceive) { /* reception interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
+#endif
+                    /* determine the channel & change to that context */
+                    save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
                     info = &cy_port[i];
                     info->last_active = jiffies;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
                     /* if there is nowhere to put the data, discard it */
                     if(info->tty == 0){
-                        j = (base_addr[CyRIVR<<index] & CyIVRMask);
+                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
-                            data = base_addr[CyRDSR<<index];
+                            data = cy_readb(base_addr+(CyRDSR<<index));
                         } else { /* normal character reception */
-                            char_count = base_addr[CyRDCR<<index];
+                            char_count = cy_readb(base_addr+(CyRDCR<<index));
                             while(char_count--){
-                                data = base_addr[CyRDSR<<index];
+                                data = cy_readb(base_addr+(CyRDSR<<index));
                             }
                         }
                     }else{ /* there is an open port for this data */
                         tty = info->tty;
-                        j = (base_addr[CyRIVR<<index] & CyIVRMask);
+                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
-                            data = base_addr[CyRDSR<<index];
+                            data = cy_readb(base_addr+(CyRDSR<<index));
                             if(data & info->ignore_status_mask){
                                 continue;
                             }
                             if (tty->flip.count < TTY_FLIPBUF_SIZE){
-				tty->flip.count++;
-				if (data & info->read_status_mask){
-				    if(data & CyBREAK){
-					*tty->flip.flag_buf_ptr++ =
-								TTY_BREAK;
-					*tty->flip.char_buf_ptr++ =
-							base_addr[CyRDSR<<index];
-					if (info->flags & ASYNC_SAK){
-					    do_SAK(tty);
-					}
-				    }else if(data & CyFRAME){
-					*tty->flip.flag_buf_ptr++ =
-								TTY_FRAME;
-					*tty->flip.char_buf_ptr++ =
-							base_addr[CyRDSR<<index];
-				    }else if(data & CyPARITY){
-					*tty->flip.flag_buf_ptr++ =
-								TTY_PARITY;
-					*tty->flip.char_buf_ptr++ =
-							base_addr[CyRDSR<<index];
-				    }else if(data & CyOVERRUN){
-					*tty->flip.flag_buf_ptr++ =
-								TTY_OVERRUN;
-					*tty->flip.char_buf_ptr++ = 0;
-					/* If the flip buffer itself is
-					   overflowing, we still loose
-					   the next incoming character.
-					 */
-					if(tty->flip.count < TTY_FLIPBUF_SIZE){
-					    tty->flip.count++;
-					    *tty->flip.flag_buf_ptr++ =
-					                         TTY_NORMAL;
-					    *tty->flip.char_buf_ptr++ =
-							base_addr[CyRDSR<<index];
-					}
-				    /* These two conditions may imply */
-				    /* a normal read should be done. */
-				    /* }else if(data & CyTIMEOUT){ */
-				    /* }else if(data & CySPECHAR){ */
-				    }else{
-					*tty->flip.flag_buf_ptr++ = 0;
-					*tty->flip.char_buf_ptr++ = 0;
-				    }
-				}else{
-				    *tty->flip.flag_buf_ptr++ = 0;
-				    *tty->flip.char_buf_ptr++ = 0;
-				}
+                                tty->flip.count++;
+                                if (data & info->read_status_mask){
+                                    if(data & CyBREAK){
+                                        *tty->flip.flag_buf_ptr++ =
+							    TTY_BREAK;
+                                        *tty->flip.char_buf_ptr++ =
+					  cy_readb(base_addr+(CyRDSR<<index));
+                                        if (info->flags & ASYNC_SAK){
+                                            do_SAK(tty);
+                                        }
+                                    }else if(data & CyFRAME){
+                                        *tty->flip.flag_buf_ptr++ =
+							    TTY_FRAME;
+                                        *tty->flip.char_buf_ptr++ =
+					  cy_readb(base_addr+(CyRDSR<<index));
+					info->idle_stats.frame_errs++;
+                                    }else if(data & CyPARITY){
+                                        *tty->flip.flag_buf_ptr++ =
+							    TTY_PARITY;
+                                        *tty->flip.char_buf_ptr++ =
+					  cy_readb(base_addr+(CyRDSR<<index));
+					info->idle_stats.parity_errs++;
+                                    }else if(data & CyOVERRUN){
+                                        *tty->flip.flag_buf_ptr++ =
+							    TTY_OVERRUN;
+                                        *tty->flip.char_buf_ptr++ = 0;
+                                        /* If the flip buffer itself is
+                                           overflowing, we still loose
+                                           the next incoming character.
+                                         */
+                                        if(tty->flip.count
+					           < TTY_FLIPBUF_SIZE){
+                                            tty->flip.count++;
+                                            *tty->flip.flag_buf_ptr++ =
+							     TTY_NORMAL;
+                                           *tty->flip.char_buf_ptr++ =
+					    cy_readb(base_addr+(CyRDSR<<index));
+                                        }
+					info->idle_stats.overruns++;
+                                    /* These two conditions may imply */
+                                    /* a normal read should be done. */
+                                    /* }else if(data & CyTIMEOUT){ */
+                                    /* }else if(data & CySPECHAR){ */
+                                    }else{
+                                        *tty->flip.flag_buf_ptr++ = 0;
+                                        *tty->flip.char_buf_ptr++ = 0;
+                                    }
+                                }else{
+                                    *tty->flip.flag_buf_ptr++ = 0;
+                                    *tty->flip.char_buf_ptr++ = 0;
+                                }
                             }else{
-                                /* there was a software buffer overrun
-				    and nothing could be done about it!!! */
+                                /* there was a software buffer
+				   overrun and nothing could be
+				   done about it!!! */
+				info->idle_stats.overruns++;
                             }
                         } else { /* normal character reception */
-                            /* load # characters available from the chip */
-                            char_count = base_addr[CyRDCR<<index];
+                            /* load # chars available from the chip */
+                            char_count = cy_readb(base_addr+(CyRDCR<<index));
 
-#ifdef CYCLOM_ENABLE_MONITORING
-			    ++info->mon.int_count;
-			    info->mon.char_count += char_count;
-			    if (char_count > info->mon.char_max)
-			       info->mon.char_max = char_count;
-			    info->mon.char_last = char_count;
+#ifdef CY_ENABLE_MONITORING
+                            ++info->mon.int_count;
+                            info->mon.char_count += char_count;
+                            if (char_count > info->mon.char_max)
+                               info->mon.char_max = char_count;
+                            info->mon.char_last = char_count;
 #endif
+			    info->idle_stats.recv_bytes += char_count;
+			    info->idle_stats.recv_idle   = jiffies;
+
                             while(char_count--){
-				if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+                                if (tty->flip.count >= TTY_FLIPBUF_SIZE){
                                         break;
                                 }
-				tty->flip.count++;
-                                data = base_addr[CyRDSR<<index];
-				*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-				*tty->flip.char_buf_ptr++ = data;
-#ifdef CYCLOM_16Y_HACK
-				udelay(10L);
+                                tty->flip.count++;
+                                data = cy_readb(base_addr+(CyRDSR<<index));
+                                *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+                                *tty->flip.char_buf_ptr++ = data;
+#ifdef CY_16Y_HACK
+                                udelay(10L);
 #endif
                             }
                         }
-                        queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+                        queue_task(&tty->flip.tqueue, &tq_timer);
                     }
                     /* end of service */
-                    base_addr[CyRIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = (save_car);
+                    cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
                 }
 
 
-                if (status & CySRTransmit) {     /* transmission interrupt */
-                    /* Since we only get here when the transmit buffer is empty,
-                        we know we can always stuff a dozen characters. */
+                if (status & CySRTransmit) { /* transmission interrupt */
+                    /* Since we only get here when the transmit buffer
+                       is empty, we know we can always stuff a dozen
+                       characters. */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
+#endif
 
-                    /* determine the channel and change to that context */
-                    save_xir = (u_char) base_addr[CyTIR<<index];
+                    /* determine the channel & change to that context */
+                    save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
-                    /* validate the port number (as configured and open) */
+                    /* validate the port# (as configured and open) */
                     if( (i < 0) || (NR_PORTS <= i) ){
-                        base_addr[CySRER<<index] &= ~CyTxMpty;
+                        cy_writeb((u_long)base_addr+(CySRER<<index),
+                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                         goto txend;
                     }
                     info = &cy_port[i];
                     info->last_active = jiffies;
                     if(info->tty == 0){
-                        base_addr[CySRER<<index] &= ~CyTxMpty;
+                        cy_writeb((u_long)base_addr+(CySRER<<index),
+                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                         goto txdone;
                     }
 
-                    /* load the on-chip space available for outbound data */
+                    /* load the on-chip space for outbound data */
                     char_count = info->xmit_fifo_size;
 
 
                     if(info->x_char) { /* send special char */
                         outch = info->x_char;
-                        base_addr[CyTDR<<index] = outch;
+                        cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         char_count--;
                         info->x_char = 0;
                     }
 
-		    if (info->x_break){
-			/*  The Cirrus chip requires the "Embedded Transmit
-			    Commands" of start break, delay, and end break
-			    sequences to be sent.  The duration of the
-			    break is given in TICs, which runs at HZ
-			    (typically 100) and the PPR runs at 200 Hz,
-			    so the delay is duration * 200/HZ, and thus a
-			    break can run from 1/100 sec to about 5/4 sec.
-			 */
-			base_addr[CyTDR<<index] = 0; /* start break */
-			base_addr[CyTDR<<index] = 0x81;
-			base_addr[CyTDR<<index] = 0; /* delay a bit */
-			base_addr[CyTDR<<index] = 0x82;
-			base_addr[CyTDR<<index] = info->x_break*200/HZ;
-			base_addr[CyTDR<<index] = 0; /* terminate break */
-			base_addr[CyTDR<<index] = 0x83;
-			char_count -= 7;
-			info->x_break = 0;
-		    }
+                    if (info->x_break){
+                        /*  The Cirrus chip requires the "Embedded
+			    Transmit Commands" of start break, delay,
+			    and end break sequences to be sent.  The
+			    duration of the break is given in TICs,
+			    which runs at HZ (typically 100) and the
+			    PPR runs at 200 Hz, so the delay is
+			    duration * 200/HZ, and thus a break can
+			    run from 1/100 sec to about 5/4 sec.
+			    For CD1400 J or later, replace the 200 Hz
+			    by 500 Hz.
+                         */
+			/* start break */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
+			/* delay a bit */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
+                        if (info->chip_rev >= CD1400_REV_J ) {
+			    /* It is a CD1400 rev. J or later */
+                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
+				      info->x_break*500/HZ);
+			} else {
+                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
+				      info->x_break*200/HZ);
+			}
+			/* finish break */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
+                        char_count -= 7;
+                        info->x_break = 0;
+                    }
 
                     while (char_count-- > 0){
-                        if (!info->xmit_cnt){
-			    base_addr[CySRER<<index] &= ~CyTxMpty;
+			if (!info->xmit_cnt){
+			    cy_writeb((u_long)base_addr+(CySRER<<index),
+				cy_readb(base_addr+(CySRER<<index)) & 
+					~CyTxMpty);
 			    goto txdone;
-                        }
+			}
 			if (info->xmit_buf == 0){
-			    base_addr[CySRER<<index] &= ~CyTxMpty;
-			    goto txdone;
+			    cy_writeb((u_long)base_addr+(CySRER<<index),
+				cy_readb(base_addr+(CySRER<<index)) & 
+					~CyTxMpty);
+			   goto txdone;
 			}
 			if (info->tty->stopped || info->tty->hw_stopped){
-			    base_addr[CySRER<<index] &= ~CyTxMpty;
-			    goto txdone;
+			    cy_writeb((u_long)base_addr+(CySRER<<index),
+				cy_readb(base_addr+(CySRER<<index)) & 
+					~CyTxMpty);
+			   goto txdone;
 			}
-                        /* Because the Embedded Transmit Commands have been
-                           enabled, we must check to see if the escape
-                           character, NULL, is being sent.  If it is, we
-                           must ensure that there is room for it to be
-                           doubled in the output stream.  Therefore we
-                           no longer advance the pointer when the character
-                           is fetched, but rather wait until after the check
-                           for a NULL output character. (This is necessary
-                           because there may not be room for the two chars
-                           needed to send a NULL.
-		         */
+                        /* Because the Embedded Transmit Commands have
+                           been enabled, we must check to see if the
+			   escape character, NULL, is being sent.  If it
+			   is, we must ensure that there is room for it
+			   to be doubled in the output stream.  Therefore
+			   we no longer advance the pointer when the
+			   character is fetched, but rather wait until
+			   after the check for a NULL output character.
+			   This is necessary because there may not be
+			   room for the two chars needed to send a NULL.)
+                         */
                         outch = info->xmit_buf[info->xmit_tail];
                         if( outch ){
-			    info->xmit_cnt--;
-			    info->xmit_tail = (info->xmit_tail + 1)
-						      & (PAGE_SIZE - 1);
-			    base_addr[CyTDR<<index] = outch;
+                            info->xmit_cnt--;
+                            info->xmit_tail = (info->xmit_tail + 1)
+                                                      & (SERIAL_XMIT_SIZE - 1);
+                            cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         }else{
                             if(char_count > 1){
-				info->xmit_cnt--;
-				info->xmit_tail = (info->xmit_tail + 1)
-							  & (PAGE_SIZE - 1);
-				base_addr[CyTDR<<index] = outch;
-				base_addr[CyTDR<<index] = 0;
-				char_count--;
+                                info->xmit_cnt--;
+                                info->xmit_tail = (info->xmit_tail + 1)
+						      & (SERIAL_XMIT_SIZE - 1);
+                                cy_writeb((u_long)base_addr+(CyTDR<<index), 
+					  outch);
+                                cy_writeb((u_long)base_addr+(CyTDR<<index), 0);
+                                char_count--;
                             }else{
                             }
                         }
@@ -929,52 +1492,64 @@
                     if (info->xmit_cnt < WAKEUP_CHARS) {
                         cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
                     }
-
         txend:
                     /* end of service */
-                    base_addr[CyTIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = (save_car);
+                    cy_writeb((u_long)base_addr+(CyTIR<<index), 
+			      (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
                 }
 
                 if (status & CySRModem) {        /* modem interrupt */
 
-                    /* determine the channel and change to that context */
-                    save_xir = (u_char) base_addr[CyMIR<<index];
+                    /* determine the channel & change to that context */
+                    save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
-                    info = &cy_port[channel + chip * 4 + cinfo->first_line];
+                    info = &cy_port[channel + chip * 4
+		                           + cinfo->first_line];
                     info->last_active = jiffies;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
-                    mdm_change = base_addr[CyMISR<<index];
-                    mdm_status = base_addr[CyMSVR1<<index];
+                    mdm_change = cy_readb(base_addr+(CyMISR<<index));
+                    mdm_status = cy_readb(base_addr+(CyMSVR1<<index));
 
-                    if(info->tty == 0){ /* nowhere to put the data, ignore it */
+                    if(info->tty == 0){/* no place for data, ignore it*/
                         ;
                     }else{
                         if((mdm_change & CyDCD)
                         && (info->flags & ASYNC_CHECK_CD)){
                             if(mdm_status & CyDCD){
-                                cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);
-                            }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)
-                                     &&(info->flags & ASYNC_CALLOUT_NOHUP))){
-                                cy_sched_event(info, Cy_EVENT_HANGUP);
+                                cy_sched_event(info,
+				    Cy_EVENT_OPEN_WAKEUP);
+                            }else if(!((info->flags
+			                & ASYNC_CALLOUT_ACTIVE)
+				 &&(info->flags
+				    & ASYNC_CALLOUT_NOHUP))){
+                                cy_sched_event(info,
+				    Cy_EVENT_HANGUP);
                             }
                         }
                         if((mdm_change & CyCTS)
                         && (info->flags & ASYNC_CTS_FLOW)){
                             if(info->tty->hw_stopped){
                                 if(mdm_status & CyCTS){
-                                    /* !!! cy_start isn't used because... */
+                                    /* cy_start isn't used
+				         because... !!! */
                                     info->tty->hw_stopped = 0;
-				    base_addr[CySRER<<index] |= CyTxMpty;
-				    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+                                  cy_writeb((u_long)base_addr+(CySRER<<index),
+                                       cy_readb(base_addr+(CySRER<<index)) | 
+                                       CyTxMpty);
+                                    cy_sched_event(info,
+				        Cy_EVENT_WRITE_WAKEUP);
                                 }
                             }else{
                                 if(!(mdm_status & CyCTS)){
-                                    /* !!! cy_stop isn't used because... */
+                                    /* cy_stop isn't used
+				         because ... !!! */
                                     info->tty->hw_stopped = 1;
-				    base_addr[CySRER<<index] &= ~CyTxMpty;
+                                  cy_writeb((u_long)base_addr+(CySRER<<index),
+                                       cy_readb(base_addr+(CySRER<<index)) & 
+                                       ~CyTxMpty);
                                 }
                             }
                         }
@@ -984,225 +1559,426 @@
                         }
                     }
                     /* end of service */
-                    base_addr[CyMIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = save_car;
+                    cy_writeb((u_long)base_addr+(CyMIR<<index), 
+			      (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_car);
                 }
             }          /* end while status != 0 */
         }            /* end loop for chips... */
     } while(had_work);
 
    /* clear interrupts */
-   *(card_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */
+   cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0);
+                                /* Cy_ClrIntr is 0x1800 */
+} /* cyy_interrupt */
+
+/***********************************************************/
+/********* End of block of Cyclom-Y specific code **********/
+/******** Start of block of Cyclades-Z specific code *********/
+/***********************************************************/
 
-} /* cy_interrupt */
-
-/*
- * This routine is used to handle the "bottom half" processing for the
- * serial driver, known also the "software interrupt" processing.
- * This processing is done at the kernel interrupt level, after the
- * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
- * is where time-consuming activities which can not be done in the
- * interrupt driver proper are done; the interrupt driver schedules
- * them using cy_sched_event(), and they get done here.
- *
- * This is done through one level of indirection--the task queue.
- * When a hardware interrupt service routine wants service by the
- * driver's bottom half, it enqueues the appropriate tq_struct (one
- * per port) to the tq_cyclades work queue and sets a request flag
- * via mark_bh for processing that queue.  When the time is right,
- * do_cyclades_bh is called (because of the mark_bh) and it requests
- * that the work queue be processed.
- *
- * Although this may seem unwieldy, it gives the system a way to
- * pass an argument (in this case the pointer to the cyclades_port
- * structure) to the bottom half of the driver.  Previous kernels
- * had to poll every port to see if that port needed servicing.
- */
-static void
-do_cyclades_bh(void)
-{
-    run_task_queue(&tq_cyclades);
-} /* do_cyclades_bh */
 
-static void
-do_softint(void *private_)
+static int
+cyz_fetch_msg( struct cyclades_card *cinfo,
+	    uclong *channel, ucchar *cmd, uclong *param)
 {
-  struct cyclades_port *info = (struct cyclades_port *) private_;
-  struct tty_struct    *tty;
-
-    tty = info->tty;
-    if (!tty)
-	return;
-
-    if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
-	tty_hangup(info->tty);
-	wake_up_interruptible(&info->open_wait);
-	info->flags &= ~(ASYNC_NORMAL_ACTIVE|
-			     ASYNC_CALLOUT_ACTIVE);
-    }
-    if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
-	wake_up_interruptible(&info->open_wait);
-    }
-    if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
-	if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
-	&& tty->ldisc.write_wakeup){
-	    (tty->ldisc.write_wakeup)(tty);
-	}
-	wake_up_interruptible(&tty->write_wait);
+  struct FIRM_ID *firm_id;
+  struct ZFW_CTRL *zfw_ctrl;
+  struct BOARD_CTRL *board_ctrl;
+  unsigned long loc_doorbell;
+
+    firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+    if (!ISZLOADED(*cinfo)){
+	return (-1);
+    }
+    zfw_ctrl = (struct ZFW_CTRL *)
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+    board_ctrl = &zfw_ctrl->board_ctrl;
+
+    loc_doorbell = cy_readl(&((struct RUNTIME_9060 *)
+                     (cinfo->ctl_addr))->loc_doorbell);
+    if (loc_doorbell){
+	*cmd = (char)(0xff & loc_doorbell);
+	*channel = cy_readl(&board_ctrl->fwcmd_channel);
+	*param = (uclong)cy_readl(&board_ctrl->fwcmd_param);
+	cy_writel(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->loc_doorbell, 
+                 0xffffffff);
+	return 1;
     }
-} /* do_softint */
+    return 0;
+} /* cyz_fetch_msg */
 
 
-/*
- * Grab all interrupts in preparation for doing an automatic irq
- * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
- * mask of irq's which were grabbed and should therefore be freed
- * using free_all_interrupts().
- */
 static int
-grab_all_interrupts(int dontgrab)
+cyz_issue_cmd( struct cyclades_card *cinfo,
+	    uclong channel, ucchar cmd, uclong param)
 {
-  int irq_lines = 0;
-  int i, mask;
-    
-    for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-	if (!(mask & dontgrab)
-	&& !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe", NULL)) {
-	    irq_lines |= mask;
-	}
+  struct FIRM_ID *firm_id;
+  struct ZFW_CTRL *zfw_ctrl;
+  struct BOARD_CTRL *board_ctrl;
+  volatile uclong *pci_doorbell;
+  int index;
+
+    firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+    if (!ISZLOADED(*cinfo)){
+	return (-1);
+    }
+    zfw_ctrl = (struct ZFW_CTRL *)
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+    board_ctrl = &zfw_ctrl->board_ctrl;
+
+    index = 0;
+    pci_doorbell = (uclong *)(&((struct RUNTIME_9060 *)
+                               (cinfo->ctl_addr))->pci_doorbell);
+    while( (cy_readl(pci_doorbell) & 0xff) != 0){
+        if (index++ == 1000){
+            return(-1);
+        }
+	udelay(50L);
     }
-    return irq_lines;
-} /* grab_all_interrupts */
+    cy_writel((u_long)&board_ctrl->hcmd_channel, channel);
+    cy_writel((u_long)&board_ctrl->hcmd_param , param);
+    cy_writel((u_long)pci_doorbell, (long)cmd);
+
+    return(0);
+} /* cyz_issue_cmd */
+
+
+#if 0
+static int
+cyz_update_channel( struct cyclades_card *cinfo,
+	    u_long channel, u_char mode, u_char cmd)
+{ 
+  struct FIRM_ID *firm_id =
+      (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+  struct ZFW_CTRL *zfw_ctrl;
+  struct CH_CTRL *ch_ctrl;
+
+    if (!ISZLOADED(*cinfo)){
+	return (-1);
+    }
+    zfw_ctrl = (struct ZFW_CTRL *)
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+    ch_ctrl = zfw_ctrl->ch_ctrl;
+
+    cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode);
+
+    return cyz_issue_cmd(cinfo, channel, cmd, 0L);
+
+} /* cyz_update_channel */
+#endif
+
 
-/*
- * Release all interrupts grabbed by grab_all_interrupts
- */
 static void
-free_all_interrupts(int irq_lines)
+cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-  int i;
-    
-    for (i = 0; i < 16; i++) {
-	if (irq_lines & (1 << i))
-	    free_irq(i,NULL);
-    }
-} /* free_all_interrupts */
+} /* cyz_interrupt */
 
-/*
- * This routine returns a bitfield of "wild interrupts".  Basically,
- * any unclaimed interrupts which is flapping around.
- */
-static int
-check_wild_interrupts(void)
+
+static void
+cyz_poll(unsigned long arg)
 {
-  int	i, mask;
-  int	wild_interrupts = 0;
-  int	irq_lines;
-  unsigned long timeout;
-  unsigned long flags;
-	
-    /*Turn on interrupts (they may be off) */
-    save_flags(flags); sti();
+  static volatile struct FIRM_ID *firm_id;
+  static volatile struct ZFW_CTRL *zfw_ctrl;
+  static volatile struct BOARD_CTRL *board_ctrl;
+  static volatile struct CH_CTRL *ch_ctrl;
+  static volatile struct BUF_CTRL *buf_ctrl;
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info;
+  struct tty_struct *tty;
+  int card, port;
+  int char_count;
+#ifdef BLOCKMOVE
+  int small_count;
+#endif
+  char data;
+  uclong channel;
+  ucchar cmd;
+  uclong param;
+  uclong hw_ver, fw_ver;
+  volatile uclong tx_put, tx_get, tx_bufsize;
+  volatile uclong rx_put, rx_get, rx_bufsize;
+
+    cyz_timerlist.expires = jiffies + (HZ);
+    for (card = 0 ; card < NR_CARDS ; card++){
+	cinfo = &cy_card[card];
+	if (!IS_CYC_Z(*cinfo)) continue;
+
+
+	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+        if (!ISZLOADED(*cinfo)) {
+	    cinfo->inact_ctrl = 0;
+	    continue;
+	}
 
-	irq_lines = grab_all_interrupts(0);
-       
-	/*
-	 * Delay for 0.1 seconds -- we use a busy loop since this may 
-	 * occur during the bootup sequence
-	 */
-	timeout = jiffies+10;
-	while (timeout >= jiffies)
-	    ;
-	
-	cy_triggered = 0;	/* Reset after letting things settle */
-
-	timeout = jiffies+10;
-	while (timeout >= jiffies)
-		;
-	
-	for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-	    if ((cy_triggered & (1 << i)) &&
-		(irq_lines & (1 << i))) {
-		    wild_interrupts |= mask;
+	zfw_ctrl = (struct ZFW_CTRL *)
+	           (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	board_ctrl = &(zfw_ctrl->board_ctrl);
+	fw_ver = cy_readl(&board_ctrl->fw_version);
+	hw_ver = cy_readl(&((struct RUNTIME_9060 *)
+                            (cinfo->ctl_addr))->mail_box_0);
+
+	/* Enables the firmware inactivity control */
+	if ((fw_ver > 0x00000310L) && (!cinfo->inact_ctrl)) {
+	    param = cyz_issue_cmd( &cy_card[card], 0L, C_CM_TINACT, 0L);
+	    cinfo->inact_ctrl = 1;
+	}
+
+	while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1){
+	    char_count = 0;
+	    info = &cy_port[ channel + cinfo->first_line ];
+            if((tty = info->tty) == 0) continue;
+	    ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+	    buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
+	    info->jiffies[0] = jiffies;
+
+	    switch(cmd){
+	    case C_CM_PR_ERROR:
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+		*tty->flip.char_buf_ptr++ = 0;
+		char_count++;
+	        break;
+	    case C_CM_FR_ERROR:
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+		*tty->flip.char_buf_ptr++ = 0;
+		char_count++;
+	        break;
+	    case C_CM_RXBRK:
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+		*tty->flip.char_buf_ptr++ = 0;
+		char_count++;
+	        break;
+	    case C_CM_MDCD:
+		if (info->flags & ASYNC_CHECK_CD){
+		    if ((fw_ver > 241 ? 
+                          ((u_long)param) : 
+                          cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
+			/* SP("Open Wakeup\n"); */
+			cy_sched_event(info,
+			    Cy_EVENT_OPEN_WAKEUP);
+		    }else if(!((info->flags
+				& ASYNC_CALLOUT_ACTIVE)
+			 &&(info->flags
+			    & ASYNC_CALLOUT_NOHUP))){
+			/* SP("Hangup\n"); */
+			cy_sched_event(info,
+			    Cy_EVENT_HANGUP);
+		    }
+		}
+	        break;
+	    case C_CM_MCTS:
+		if (info->flags & ASYNC_CTS_FLOW) {
+		    if(info->tty->hw_stopped){
+			if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
+			    /* cy_start isn't used because... 
+			       HW flow is handled by the board */
+			    /* SP("Write Wakeup\n"); */
+			    cy_sched_event(info,
+				Cy_EVENT_WRITE_WAKEUP);
+			}
+		    }else{
+			if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
+			    /* cy_stop isn't used because 
+			       HW flow is handled by the board */
+			    /* SP("Write stop\n"); */
+			}
+		    }
+		}
+	        break;
+	    case C_CM_MRI:
+	        break;
+	    case C_CM_MDSR:
+	        break;
+#ifdef Z_WAKE
+	    case C_CM_IOCTLW:
+		cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP);
+	        break;
+#endif
+	    case C_CM_FATAL:
+		/* should do something with this !!! */
+	        break;
+	    }
+	    if(char_count){
+		queue_task(&tty->flip.tqueue, &tq_timer);
 	    }
 	}
-	free_all_interrupts(irq_lines);
-    restore_flags(flags);
-    return wild_interrupts;
-} /* check_wild_interrupts */
+	for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){
+	    info = &cy_port[ port + cinfo->first_line ];
+            tty = info->tty;
+	    ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
+	    buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
+
+/* Removed due to compilation problems in Alpha systems */
+//	    if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+
+	    rx_get = cy_readl(&buf_ctrl->rx_get);
+	    rx_put = cy_readl(&buf_ctrl->rx_put);
+	    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
+	    if (rx_put >= rx_get)
+	    	char_count = rx_put - rx_get;
+	    else
+	    	char_count = rx_put - rx_get + rx_bufsize;
+
+	    if ( char_count ){
+
+		info->last_active = jiffies;
+		info->jiffies[1] = jiffies;
+
+#ifdef CY_ENABLE_MONITORING
+		info->mon.int_count++;
+		info->mon.char_count += char_count;
+		if (char_count > info->mon.char_max)
+		   info->mon.char_max = char_count;
+		info->mon.char_last = char_count;
+#endif
+		info->idle_stats.recv_bytes += char_count;
+		info->idle_stats.recv_idle   = jiffies;
+		if( tty == 0){
+		    /* flush received characters */
+		    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
+		    /* SP("-"); */
+		    info->rflush_count++;
+		}else{
+#ifdef BLOCKMOVE
+		/* we'd like to use memcpy(t, f, n) and memset(s, c, count)
+		   for performance, but because of buffer boundaries, there
+		   may be several steps to the operation */
+		    while(0 < (small_count
+			= cy_min((rx_bufsize - rx_get),
+			         cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
+				        char_count)))){
+
+			memcpy_fromio(tty->flip.char_buf_ptr,
+			              (char *)(cinfo->base_addr
+				       + cy_readl(&buf_ctrl->rx_bufaddr)
+				       + rx_get),
+			              small_count);
+
+			tty->flip.char_buf_ptr += small_count;
+			memset(tty->flip.flag_buf_ptr,
+			       TTY_NORMAL,
+			       small_count);
+			tty->flip.flag_buf_ptr += small_count;
+			rx_get = (rx_get + small_count) & (rx_bufsize - 1);
+			char_count -= small_count;
+			tty->flip.count += small_count;
+		    }
+#else
+		    while(char_count--){
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+				break;
+			}
+			data = cy_readb(cinfo->base_addr +
+			           cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
+			rx_get = (rx_get + 1) & (rx_bufsize - 1);
+			tty->flip.count++;
+			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+			*tty->flip.char_buf_ptr++ = data;
+		    }
+#endif
+		    queue_task(&tty->flip.tqueue, &tq_timer);
+		}
+		/* Update rx_get */
+		cy_writel(&buf_ctrl->rx_get, rx_get);
+	    }
 
-/*
- * This routine is called by do_auto_irq(); it attempts to determine
- * which interrupt a serial port is configured to use.  It is not
- * fool-proof, but it works a large part of the time.
- */
-static int
-get_auto_irq(unsigned char *address)
-{
-  unsigned long timeout;
-  unsigned char *base_addr;
-  int		index;
+/* Removed due to compilation problems in Alpha systems */
+//	    if ((char_count = SPACE_IN_BUF(buf_ctrl))){
 
-    index = 0;	/* IRQ probing is only for ISA */
-    base_addr = address;
-    intr_base_addr = address;
-	
-    /*
-     * Enable interrupts and see who answers
-     */
-    cy_irq_triggered = 0;
-    cli();
-	base_addr[CyCAR<<index] = 0;
-	write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
-	base_addr[CySRER<<index] |= CyTxMpty;
-	probe_ready = 1;
-    sti();
-    
-    timeout = jiffies+2;
-    while (timeout >= jiffies) {
-	if (cy_irq_triggered)
-	    break;
-    }
-    probe_ready = 0;
-    return(cy_irq_triggered);
-} /* get_auto_irq */
+	    tx_get = cy_readl(&buf_ctrl->tx_get);
+	    tx_put = cy_readl(&buf_ctrl->tx_put);
+	    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+	    if (tx_put >= tx_get)
+	    	char_count = tx_get - tx_put - 1 + tx_bufsize;
+	    else
+	    	char_count = tx_get - tx_put - 1;
 
-/*
- * Calls get_auto_irq() multiple times, to make sure we don't get
- * faked out by random interrupts
- */
-static int
-do_auto_irq(unsigned char *address)
-{
-  int 			irq_lines = 0;
-  int			irq_try_1 = 0, irq_try_2 = 0;
-  int			retries;
-  unsigned long flags;
+	    if ( char_count ){
 
-    /* Turn on interrupts (they may be off) */
-    save_flags(flags); sti();
+		if( tty == 0 ){
+		    goto ztxdone;
+		}
 
-	probe_ready = 0;
+		if(info->x_char) { /* send special char */
+		    data = info->x_char;
 
-        cy_wild_int_mask = check_wild_interrupts();
+		    cy_writeb((cinfo->base_addr +
+		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
+		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
+		    info->x_char = 0;
+		    char_count--;
+		    info->last_active = jiffies;
+		    info->jiffies[2] = jiffies;
+		}
+		if (info->x_break){
+		    printk("cyc cyz_poll shouldn't see x_break\n");
+		    info->x_break = 0;
+		    info->last_active = jiffies;
+		    info->jiffies[2] = jiffies;
+		}
+#ifdef BLOCKMOVE
+		while(0 < (small_count
+		    = cy_min((tx_bufsize - tx_put),
+			     cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
+			             cy_min(info->xmit_cnt, char_count))))){
+
+		    memcpy_toio((char *)(cinfo->base_addr
+			         + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
+			        &info->xmit_buf[info->xmit_tail],
+			        small_count);
+
+		    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
+		    char_count -= small_count;
+		    info->xmit_cnt -= small_count;
+		    info->xmit_tail =
+		       (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
+		    info->last_active = jiffies;
+		    info->jiffies[2] = jiffies;
+		}
+#else
+		while (info->xmit_cnt && char_count){
+		    data = info->xmit_buf[info->xmit_tail];
+		    info->xmit_cnt--;
+		    info->xmit_tail =
+			(info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+
+		    cy_writeb(cinfo->base_addr +
+		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, 
+			      data);
+		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
+		    char_count--;
+		    info->last_active = jiffies;
+		    info->jiffies[2] = jiffies;
+		}
 
-	irq_lines = grab_all_interrupts(cy_wild_int_mask);
-	
-	for (retries = 0; retries < 5; retries++) {
-	    if (!irq_try_1)
-		irq_try_1 = get_auto_irq(address);
-	    if (!irq_try_2)
-		irq_try_2 = get_auto_irq(address);
-	    if (irq_try_1 && irq_try_2) {
-		if (irq_try_1 == irq_try_2)
-		    break;
-		irq_try_1 = irq_try_2 = 0;
+#endif
+	    ztxdone:
+		if (info->xmit_cnt < WAKEUP_CHARS) {
+		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+		}
+		/* Update tx_put */
+		cy_writel(&buf_ctrl->tx_put, tx_put);
 	    }
 	}
-    restore_flags(flags);
-    free_all_interrupts(irq_lines);
-    return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
-} /* do_auto_irq */
+	/* poll every 40 ms */
+	cyz_timerlist.expires = jiffies + cyz_polling_cycle;
+
+	/* refresh inactivity counter */
+	if (cinfo->inact_ctrl) {
+		cy_writel(&board_ctrl->inactivity, (uclong) ZF_TINACT);
+	}
+    }
+    add_timer(&cyz_timerlist);
+
+    return;
+} /* cyz_poll */
+
+
+/********** End of block of Cyclades-Z specific code *********/
+/***********************************************************/
 
 
 /* This is called whenever a port becomes active;
@@ -1216,72 +1992,144 @@
   int card,chip,channel,index;
 
     if (info->flags & ASYNC_INITIALIZED){
-	return 0;
+        return 0;
     }
 
     if (!info->type){
-	if (info->tty){
-	    set_bit(TTY_IO_ERROR, &info->tty->flags);
-	}
-	return 0;
+        if (info->tty){
+            set_bit(TTY_IO_ERROR, &info->tty->flags);
+        }
+        return 0;
     }
     if (!info->xmit_buf){
-	info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
-	if (!info->xmit_buf){
-	    return -ENOMEM;
-	}
+        info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
+        if (!info->xmit_buf){
+            return -ENOMEM;
+        }
     }
 
-    config_setup(info);
+    set_line_char(info);
 
     card = info->card;
     channel = (info->line) - (cy_card[card].first_line);
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
 		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
 
-#ifdef SERIAL_DEBUG_OPEN
-    printk("startup card %d, chip %d, channel %d, base_addr %lx",
-	 card, chip, channel, (long)base_addr);/**/
+#ifdef CY_DEBUG_OPEN
+	printk("cyc startup card %d, chip %d, channel %d, base_addr %lx\n",
+	     card, chip, channel, (long)base_addr);/**/
 #endif
 
-    save_flags(flags); cli();
-	base_addr[CyCAR<<index] = (u_char)channel;
+	save_flags(flags); cli();
+	    cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
 
-	base_addr[CyRTPR<<index] = (info->default_timeout
-			     ? info->default_timeout
-			     : 0x02); /* 10ms rx timeout */
+	    cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
+				 ? info->default_timeout
+				 : 0x02)); /* 10ms rx timeout */
 
-	write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
+	    cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
 
-	base_addr[CyCAR<<index] = (u_char)channel; /* !!! Is this needed? */
-	base_addr[CyMSVR1<<index] = CyRTS;
-	base_addr[CyMSVR2<<index] = CyDTR;
+	    cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+	    cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
+	    cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
 
-#ifdef SERIAL_DEBUG_DTR
-        printk("cyc: %d: raising DTR\n", __LINE__);
-        printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+#ifdef CY_DEBUG_DTR
+	    printk("cyc:startup raising DTR\n");
+	    printk("     status: 0x%x, 0x%x\n",
+		   cy_readb(base_addr+(CyMSVR1<<index)), 
+                   cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 
-	base_addr[CySRER<<index] |= CyRxData;
-	info->flags |= ASYNC_INITIALIZED;
+	    cy_writeb((u_long)base_addr+(CySRER<<index),
+               cy_readb(base_addr+(CySRER<<index)) | CyRxData);
+	    info->flags |= ASYNC_INITIALIZED;
+
+	    if (info->tty){
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	    }
+	    info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	    memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
+	    info->idle_stats.in_use    = 
+	    info->idle_stats.recv_idle =
+	    info->idle_stats.xmit_idle = jiffies;
+	restore_flags(flags);
+    } else {
+      struct FIRM_ID *firm_id;
+      struct ZFW_CTRL *zfw_ctrl;
+      struct BOARD_CTRL *board_ctrl;
+      struct CH_CTRL *ch_ctrl;
+      int retval;
+
+	base_addr = (unsigned char*) (cy_card[card].base_addr);
+
+        firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
+        if (!ISZLOADED(cy_card[card])){
+	    return -ENODEV;
+	}
+
+	zfw_ctrl =
+	    (struct ZFW_CTRL *)
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	board_ctrl = &zfw_ctrl->board_ctrl;
+	ch_ctrl = zfw_ctrl->ch_ctrl;
+
+#ifdef CY_DEBUG_OPEN
+	printk("cyc startup Z card %d, channel %d, base_addr %lx\n",
+	     card, channel, (long)base_addr);/**/
+#endif
+
+	cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE);
+#ifdef Z_WAKE
+	cy_writel(&ch_ctrl[channel].intr_enable, 
+		  C_IN_MDCD|C_IN_MCTS|C_IN_IOCTLW);
+#else
+	cy_writel(&ch_ctrl[channel].intr_enable, 
+		  C_IN_MDCD|C_IN_MCTS);
+#endif
+	retval = cyz_issue_cmd( &cy_card[card],
+	    channel, C_CM_IOCTL, 0L);	/* was C_CM_RESET */
+	if (retval != 0){
+	    printk("cyc:startup(1) retval was %x\n", retval);
+	}
+
+	/* set timeout !!! */
+	/* set RTS and DTR !!! */
+	cy_writel(&ch_ctrl[channel].rs_control,
+             cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR) ;
+	retval = cyz_issue_cmd(&cy_card[info->card],
+	    channel, C_CM_IOCTLM, 0L);
+	if (retval != 0){
+	    printk("cyc:startup(2) retval was %x\n", retval);
+	}
+#ifdef CY_DEBUG_DTR
+	    printk("cyc:startup raising Z DTR\n");
+#endif
 
+	/* enable send, recv, modem !!! */
+
+	info->flags |= ASYNC_INITIALIZED;
 	if (info->tty){
 	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
 	}
 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
+	info->idle_stats.in_use    = 
+	info->idle_stats.recv_idle =
+	info->idle_stats.xmit_idle = jiffies;
+    }
 
-    restore_flags(flags);
-
-#ifdef SERIAL_DEBUG_OPEN
-    printk(" done\n");
+#ifdef CY_DEBUG_OPEN
+	printk(" cyc startup done\n");
 #endif
-    return 0;
+	return 0;
 } /* startup */
 
-void
+
+static void
 start_xmit( struct cyclades_port *info )
 {
   unsigned long flags;
@@ -1290,18 +2138,25 @@
 
     card = info->card;
     channel = (info->line) - (cy_card[card].first_line);
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
 
-    save_flags(flags); cli();
-	base_addr[CyCAR<<index] = channel;
-	base_addr[CySRER<<index] |= CyTxMpty;
-    restore_flags(flags);
+	save_flags(flags); cli();
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+	    cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
+	restore_flags(flags);
+    } else {
+	/* Don't have to do anything at this time */
+    }
 } /* start_xmit */
 
+
 /*
  * This routine shuts down a serial port; interrupts are disabled,
  * and DTR is dropped if the hangup on close termio flag is on.
@@ -1314,326 +2169,661 @@
   int card,chip,channel,index;
 
     if (!(info->flags & ASYNC_INITIALIZED)){
-	return;
+        return;
     }
 
     card = info->card;
     channel = info->line - cy_card[card].first_line;
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
 
-#ifdef SERIAL_DEBUG_OPEN
-    printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n",
-	    card, chip, channel, (long)base_addr);
+#ifdef CY_DEBUG_OPEN
+    printk("cyc shutdown Y card %d, chip %d, channel %d, base_addr %lx\n",
+		card, chip, channel, (long)base_addr);
 #endif
 
-    /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
-       SENT BEFORE DROPPING THE LINE !!!  (Perhaps
-       set some flag that is read when XMTY happens.)
-       Other choices are to delay some fixed interval
-       or schedule some later processing.
-     */
-    save_flags(flags); cli();
-	if (info->xmit_buf){
-            unsigned char * temp;
-            temp = info->xmit_buf;
-            info->xmit_buf = 0;
-	    free_page((unsigned long) temp);
-	}
-
-	base_addr[CyCAR<<index] = (u_char)channel;
-	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-	    base_addr[CyMSVR1<<index] = ~CyRTS;
-	    base_addr[CyMSVR2<<index] = ~CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-            printk("cyc: %d: dropping DTR\n", __LINE__);
-            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
-#endif
-        }
-	write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index);
-         /* it may be appropriate to clear _XMIT at
-           some later date (after testing)!!! */
+	save_flags(flags); cli();
 
-	if (info->tty){
-	    set_bit(TTY_IO_ERROR, &info->tty->flags);
+	    if (info->xmit_buf){
+		unsigned char * temp;
+		temp = info->xmit_buf;
+		info->xmit_buf = 0;
+		free_page((unsigned long) temp);
+	    }
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+	    if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+		cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+#ifdef CY_DEBUG_DTR
+		printk("cyc shutdown dropping DTR\n");
+		printk("     status: 0x%x, 0x%x\n",
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
+#endif
+	    }
+	    cyy_issue_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index);
+	     /* it may be appropriate to clear _XMIT at
+	       some later date (after testing)!!! */
+
+	    if (info->tty){
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+	    }
+	    info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+    } else {
+      struct FIRM_ID *firm_id;
+      struct ZFW_CTRL *zfw_ctrl;
+      struct BOARD_CTRL *board_ctrl;
+      struct CH_CTRL *ch_ctrl;
+      int retval;
+
+	base_addr = (unsigned char*) (cy_card[card].base_addr);
+#ifdef CY_DEBUG_OPEN
+    printk("cyc shutdown Z card %d, channel %d, base_addr %lx\n",
+		card, channel, (long)base_addr);
+#endif
+
+        firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
+        if (!ISZLOADED(cy_card[card])) {
+	    return;
 	}
-	info->flags &= ~ASYNC_INITIALIZED;
-    restore_flags(flags);
 
-#ifdef SERIAL_DEBUG_OPEN
-    printk(" done\n");
+	zfw_ctrl =
+	    (struct ZFW_CTRL *)
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	board_ctrl = &(zfw_ctrl->board_ctrl);
+	ch_ctrl = zfw_ctrl->ch_ctrl;
+
+	save_flags(flags); cli();
+
+	    if (info->xmit_buf){
+		unsigned char * temp;
+		temp = info->xmit_buf;
+		info->xmit_buf = 0;
+		free_page((unsigned long) temp);
+	    }
+	    
+	    if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		cy_writel((u_long)&ch_ctrl[channel].rs_control,
+                   (uclong)(cy_readl(&ch_ctrl[channel].rs_control) & 
+                   ~(C_RS_RTS | C_RS_DTR)));
+		retval = cyz_issue_cmd(&cy_card[info->card],
+			channel, C_CM_IOCTLM, 0L);
+		if (retval != 0){
+		    printk("cyc:shutdown retval (2) was %x\n", retval);
+		}
+#ifdef CY_DEBUG_DTR
+		printk("cyc:shutdown dropping Z DTR\n");
+#endif
+	    }
+	    
+	    if (info->tty){
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+	    }
+	    info->flags &= ~ASYNC_INITIALIZED;
+
+	restore_flags(flags);
+    }
+
+#ifdef CY_DEBUG_OPEN
+    printk(" cyc shutdown done\n");
 #endif
     return;
 } /* shutdown */
 
+
 /*
- * This routine finds or computes the various line characteristics.
+ * ------------------------------------------------------------
+ * cy_open() and friends
+ * ------------------------------------------------------------
  */
-static void
-config_setup(struct cyclades_port * info)
+
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+                           struct cyclades_port *info)
 {
+  struct wait_queue wait = { current, NULL };
+  struct cyclades_card *cinfo;
   unsigned long flags;
-  unsigned char *base_addr;
-  int card,chip,channel,index;
-  unsigned cflag;
-  int   i;
-
-    if (!info->tty || !info->tty->termios){
-        return;
-    }
-    if (info->line == -1){
-        return;
-    }
-    cflag = info->tty->termios->c_cflag;
+  int chip, channel,index;
+  int retval;
+  char *base_addr;
 
-    /* baud rate */
-    i = cflag & CBAUD;
-#ifdef CBAUDEX
-/* Starting with kernel 1.1.65, there is direct support for
-   higher baud rates.  The following code supports those
-   changes.  The conditional aspect allows this driver to be
-   used for earlier as well as later kernel versions.  (The
-   mapping is slightly different from serial.c because there
-   is still the possibility of supporting 75 kbit/sec with
-   the Cyclades board.)
- */
-    if (i & CBAUDEX) {
-	if (i == B57600)
-	    i = 16;
-	else if(i == B115200) 
-	    i = 18;
-#ifdef B78600
-	else if(i == B78600) 
-	    i = 17;
-#endif
-	else
-	    info->tty->termios->c_cflag &= ~CBAUDEX;
-    }
-#endif
-    if (i == 15) {
-	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-		    i += 1;
-	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-		    i += 3;
-    }
-    info->tbpr = baud_bpr[i]; /* Tx BPR */
-    info->tco = baud_co[i]; /* Tx CO */
-    info->rbpr = baud_bpr[i]; /* Rx BPR */
-    info->rco = baud_co[i]; /* Rx CO */
-    if (baud_table[i] == 134) {
-        info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
-        /* get it right for 134.5 baud */
-    } else if (baud_table[i]) {
-        info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
-        /* this needs to be propagated into the card info */
-    } else {
-        info->timeout = 0;
-    }
-    /* By tradition (is it a standard?) a baud rate of zero
-       implies the line should be/has been closed.  A bit
-       later in this routine such a test is performed. */
-
-    /* byte size and parity */
-    info->cor5 = 0;
-    info->cor4 = 0;
-    info->cor3 = (info->default_threshold
-		  ? info->default_threshold
-		  : baud_cor3[i]); /* receive threshold */
-    info->cor2 = CyETC;
-    switch(cflag & CSIZE){
-    case CS5:
-        info->cor1 = Cy_5_BITS;
-        break;
-    case CS6:
-        info->cor1 = Cy_6_BITS;
-        break;
-    case CS7:
-        info->cor1 = Cy_7_BITS;
-        break;
-    case CS8:
-        info->cor1 = Cy_8_BITS;
-        break;
-    }
-    if(cflag & CSTOPB){
-        info->cor1 |= Cy_2_STOP;
-    }
-    if (cflag & PARENB){
-        if (cflag & PARODD){
-            info->cor1 |= CyPARITY_O;
+    /*
+     * 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) || (info->flags & ASYNC_CLOSING)) {
+	if (info->flags & ASYNC_CLOSING) {
+            interruptible_sleep_on(&info->close_wait);
+	}
+        if (info->flags & ASYNC_HUP_NOTIFY){
+            return -EAGAIN;
         }else{
-            info->cor1 |= CyPARITY_E;
+            return -ERESTARTSYS;
         }
-    }else{
-        info->cor1 |= CyPARITY_NONE;
     }
-	
-    /* CTS flow control flag */
-    if (cflag & CRTSCTS){
-	info->flags |= ASYNC_CTS_FLOW;
-	info->cor2 |= CyCtsAE;
-    }else{
-	info->flags &= ~ASYNC_CTS_FLOW;
-	info->cor2 &= ~CyCtsAE;
+
+    /*
+     * If this is a callout device, then just make sure the normal
+     * device isn't being used.
+     */
+    if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+        if (info->flags & ASYNC_NORMAL_ACTIVE){
+            return -EBUSY;
+        }
+        if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+            (info->flags & ASYNC_SESSION_LOCKOUT) &&
+            (info->session != current->session)){
+            return -EBUSY;
+        }
+        if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+            (info->flags & ASYNC_PGRP_LOCKOUT) &&
+            (info->pgrp != current->pgrp)){
+            return -EBUSY;
+        }
+        info->flags |= ASYNC_CALLOUT_ACTIVE;
+        return 0;
     }
-    if (cflag & CLOCAL)
-	info->flags &= ~ASYNC_CHECK_CD;
-    else
-	info->flags |= ASYNC_CHECK_CD;
-
-     /***********************************************
-	The hardware option, CyRtsAO, presents RTS when
-	the chip has characters to send.  Since most modems
-	use RTS as reverse (inbound) flow control, this
-	option is not used.  If inbound flow control is
-	necessary, DTR can be programmed to provide the
-	appropriate signals for use with a non-standard
-	cable.  Contact Marcio Saito for details.
-     ***********************************************/
 
-    card = info->card;
-    channel = (info->line) - (cy_card[card].first_line);
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    /*
+     * If non-blocking mode is set, then make the check up front
+     * and then exit.
+     */
+    if ((filp->f_flags & O_NONBLOCK) ||
+	(tty->flags & (1 << TTY_IO_ERROR))) {
+        if (info->flags & ASYNC_CALLOUT_ACTIVE){
+            return -EBUSY;
+        }
+        info->flags |= ASYNC_NORMAL_ACTIVE;
+        return 0;
+    }
 
+    /*
+     * 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
+     * cy_close() knows when to free things.  We restore it upon
+     * exit, either normal or abnormal.
+     */
+    retval = 0;
+    add_wait_queue(&info->open_wait, &wait);
+#ifdef CY_DEBUG_OPEN
+    printk("cyc block_til_ready before block: ttyC%d, count = %d\n",
+           info->line, info->count);/**/
+#endif
     save_flags(flags); cli();
-	base_addr[CyCAR<<index] = (u_char)channel;
+    if (!tty_hung_up_p(filp))
+	info->count--;
+    restore_flags(flags);
+#ifdef CY_DEBUG_COUNT
+    printk("cyc block_til_ready: (%d): decrementing count to %d\n",
+        current->pid, info->count);
+#endif
+    info->blocked_open++;
 
-       /* tx and rx baud rate */
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    if (!IS_CYC_Z(*cinfo)) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cinfo->bus_index;
+	base_addr = (char *)(cinfo->base_addr
+			    + (cy_chip_offset[chip]<<index));
 
-	base_addr[CyTCOR<<index] = info->tco;
-	base_addr[CyTBPR<<index] = info->tbpr;
-	base_addr[CyRCOR<<index] = info->rco;
-	base_addr[CyRBPR<<index] = info->rbpr;
-
-	/* set line characteristics  according configuration */
-
-	base_addr[CySCHR1<<index] = START_CHAR(info->tty);
-	base_addr[CySCHR2<<index] = STOP_CHAR(info->tty);
-	base_addr[CyCOR1<<index] = info->cor1;
-	base_addr[CyCOR2<<index] = info->cor2;
-	base_addr[CyCOR3<<index] = info->cor3;
-	base_addr[CyCOR4<<index] = info->cor4;
-	base_addr[CyCOR5<<index] = info->cor5;
-
-	write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index);
-
-	base_addr[CyCAR<<index] = (u_char)channel; /* !!! Is this needed? */
-
-	base_addr[CyRTPR<<index] = (info->default_timeout
-			     ? info->default_timeout
-			     : 0x02); /* 10ms rx timeout */
-
-	if (C_CLOCAL(info->tty)) {
-	    base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */
-				    /* act on 1->0 modem transitions */
-	    base_addr[CyMCOR1<<index] = CyCTS;
-				    /* act on 0->1 modem transitions */
-	    base_addr[CyMCOR2<<index] = CyCTS;
-	} else {
-	    base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */
-				    /* act on 1->0 modem transitions */
-	    base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD;
-				    /* act on 0->1 modem transitions */
-	    base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+	while (1) {
+	    save_flags(flags); cli();
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
+		    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+		    cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+#ifdef CY_DEBUG_DTR
+		    printk("cyc:block_til_ready raising DTR\n");
+		    printk("     status: 0x%x, 0x%x\n",
+			cy_readb(base_addr+(CyMSVR1<<index)), 
+                        cy_readb(base_addr+(CyMSVR2<<index)));
+#endif
+		}
+	    restore_flags(flags);
+	    current->state = TASK_INTERRUPTIBLE;
+	    if (tty_hung_up_p(filp)
+	    || !(info->flags & ASYNC_INITIALIZED) ){
+		if (info->flags & ASYNC_HUP_NOTIFY) {
+		    retval = -EAGAIN;
+		}else{
+		    retval = -ERESTARTSYS;
+		}
+		break;
+	    }
+	    save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
+		&& !(info->flags & ASYNC_CLOSING)
+		&& (C_CLOCAL(tty)
+		    || (cy_readb(base_addr+(CyMSVR1<<index)) & CyDCD))) {
+			restore_flags(flags);
+			break;
+		}
+	    restore_flags(flags);
+	    if (current->signal & ~current->blocked) {
+		retval = -ERESTARTSYS;
+		break;
+	    }
+#ifdef CY_DEBUG_OPEN
+	    printk("cyc block_til_ready blocking: ttyC%d, count = %d\n",
+		   info->line, info->count);/**/
+#endif
+	    schedule();
+	}
+    } else {
+      struct FIRM_ID *firm_id;
+      struct ZFW_CTRL *zfw_ctrl;
+      struct BOARD_CTRL *board_ctrl;
+      struct CH_CTRL *ch_ctrl;
+      int retval;
+
+	base_addr = (char *)(cinfo->base_addr);
+	firm_id = (struct FIRM_ID *)
+			(base_addr + ID_ADDRESS);
+        if (!ISZLOADED(*cinfo)){
+	    return -EINVAL;
 	}
 
-	if(i == 0){ /* baud rate is zero, turn off line */
-	    base_addr[CyMSVR2<<index] = ~CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-            printk("cyc: %d: dropping DTR\n", __LINE__);
-            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+	zfw_ctrl =
+	    (struct ZFW_CTRL *)
+		(base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	board_ctrl = &zfw_ctrl->board_ctrl;
+	ch_ctrl = zfw_ctrl->ch_ctrl;
+
+	while (1) {
+	    cy_writel(&ch_ctrl[channel].rs_control,
+               cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR);
+	    retval = cyz_issue_cmd(&cy_card[info->card],
+		    channel, C_CM_IOCTLM, 0L);
+	    if (retval != 0){
+		printk("cyc:block_til_ready retval was %x\n", retval);
+	    }
+#ifdef CY_DEBUG_DTR
+		    printk("cyc:block_til_ready raising Z DTR\n");
 #endif
-	}else{
-	    base_addr[CyMSVR2<<index] = CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-            printk("cyc: %d: raising DTR\n", __LINE__);
-            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+
+	    current->state = TASK_INTERRUPTIBLE;
+	    if (tty_hung_up_p(filp)
+	    || !(info->flags & ASYNC_INITIALIZED) ){
+		if (info->flags & ASYNC_HUP_NOTIFY) {
+		    retval = -EAGAIN;
+		}else{
+		    retval = -ERESTARTSYS;
+		}
+		break;
+	    }
+	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
+	    && !(info->flags & ASYNC_CLOSING)
+	    && (C_CLOCAL(tty)
+	      || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) {
+		break;
+	    }
+	    if (current->signal & ~current->blocked) {
+		retval = -ERESTARTSYS;
+		break;
+	    }
+#ifdef CY_DEBUG_OPEN
+	    printk("cyc block_til_ready blocking: ttyC%d, count = %d\n",
+		   info->line, info->count);/**/
 #endif
+	    schedule();
 	}
+    }
+    current->state = TASK_RUNNING;
+    remove_wait_queue(&info->open_wait, &wait);
+    if (!tty_hung_up_p(filp)){
+	info->count++;
+#ifdef CY_DEBUG_COUNT
+	printk("cyc:block_til_ready (%d): incrementing count to %d\n",
+	    current->pid, info->count);
+#endif
+    }
+    info->blocked_open--;
+#ifdef CY_DEBUG_OPEN
+    printk("cyc:block_til_ready after blocking: ttyC%d, count = %d\n",
+	   info->line, info->count);/**/
+#endif
+    if (retval)
+	return retval;
+    info->flags |= ASYNC_NORMAL_ACTIVE;
+    return 0;
+} /* block_til_ready */
 
-	if (info->tty){
-	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * performs the serial-specific initialization for the tty structure.
+ */
+int
+cy_open(struct tty_struct *tty, struct file * filp)
+{
+  struct cyclades_port  *info;
+  int retval, line;
+
+    line = MINOR(tty->device) - tty->driver.minor_start;
+    if ((line < 0) || (NR_PORTS <= line)){
+        return -ENODEV;
+    }
+    info = &cy_port[line];
+    if (info->line < 0){
+        return -ENODEV;
+    }
+    
+    /* If the card's firmware hasn't been loaded,
+       treat it as absent from the system.  This
+       will make the user pay attention.
+    */
+    if (IS_CYC_Z(cy_card[info->card])) {
+        if (!ISZLOADED(cy_card[info->card])) {
+	    if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *)
+		((cy_card[info->card]).ctl_addr))->mail_box_0)) &&
+		Z_FPGA_CHECK(cy_card[info->card])) &&
+		(ZFIRM_HLT==cy_readl(&((struct FIRM_ID *)
+		((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)))
+	    {
+		printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n");
+	    } else {
+		printk("Cyclades-Z firmware not yet loaded\n");
+	    }
+	    return -ENODEV;
 	}
+    }
+#ifdef CY_DEBUG_OTHER
+    printk("cyc:cy_open ttyC%d\n", info->line); /* */
+#endif
+    if (serial_paranoia_check(info, tty->device, "cy_open")){
+        return -ENODEV;
+    }
+#ifdef CY_DEBUG_OPEN
+    printk("cyc:cy_open ttyC%d, count = %d\n",
+        info->line, info->count);/**/
+#endif
+    info->count++;
+#ifdef CY_DEBUG_COUNT
+    printk("cyc:cy_open (%d): incrementing count to %d\n",
+        current->pid, info->count);
+#endif
+    tty->driver_data = info;
+    info->tty = tty;
 
-    restore_flags(flags);
+    /* Some drivers have (incorrect/incomplete) code to test
+       against a race condition.  Should add good code here!!! */
+    if (!tmp_buf) {
+        tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+        if (!tmp_buf){
+            return -ENOMEM;
+        }
+    }
+
+    if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+        if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+            *tty->termios = info->normal_termios;
+        else 
+            *tty->termios = info->callout_termios;
+    }
+    /*
+     * Start up serial port
+     */
+    retval = startup(info);
+    if (retval){
+        return retval;
+    }
 
-} /* config_setup */
+    MOD_INC_USE_COUNT;
 
+    retval = block_til_ready(tty, filp, info);
+    if (retval) {
+#ifdef CY_DEBUG_OPEN
+        printk("cyc:cy_open returning after block_til_ready with %d\n",
+               retval);
+#endif
+        return retval;
+    }
 
-static void
-cy_put_char(struct tty_struct *tty, unsigned char ch)
-{
-  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
-  unsigned long flags;
+    info->session = current->session;
+    info->pgrp = current->pgrp;
 
-#ifdef SERIAL_DEBUG_IO
-    printk("cy_put_char ttyC%d\n", info->line);
+#ifdef CY_DEBUG_OPEN
+    printk(" cyc:cy_open done\n");/**/
 #endif
 
-    if (serial_paranoia_check(info, tty->device, "cy_put_char"))
-	return;
+    return 0;
+} /* cy_open */
 
-    if (!tty || !info->xmit_buf)
+/*
+ * cy_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  unsigned char *base_addr;
+  int card,chip,channel,index;
+  unsigned long orig_jiffies, char_time;
+
+    if (serial_paranoia_check(info, tty->device, "cy_wait_until_sent"))
 	return;
 
-    save_flags(flags); cli();
-	if (info->xmit_cnt >= PAGE_SIZE - 1) {
-	    restore_flags(flags);
-	    return;
+    orig_jiffies = jiffies;
+    /*
+     * Set the check interval to be 1/5 of the estimated time to
+     * send a single character, and make it at least 1.  The check
+     * interval should also be less than the timeout.
+     *
+     * Note: we have to use pretty tight timings here to satisfy
+     * the NIST-PCTS.
+     */
+    char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+    char_time = char_time / 5;
+    if (char_time == 0)
+	char_time = 1;
+    if (timeout < 0)
+	timeout = 0;
+    if (timeout)
+	char_time = MIN(char_time, timeout);
+#ifdef CY_DEBUG_WAIT_UNTIL_SENT
+    printk("In cy_wait_until_sent(%d) check=%lu...", timeout, char_time);
+    printk("jiff=%lu...", jiffies);
+#endif
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char *)
+		(cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+	while (cy_readb(base_addr+(CySRER<<index)) & CyTxMpty) {
+#ifdef CY_DEBUG_WAIT_UNTIL_SENT
+	    printk("Not clean (jiff=%lu)...", jiffies);
+#endif
+	    current->state = TASK_INTERRUPTIBLE;
+	    current->counter = 0;       /* make us low-priority */
+	    current->timeout = jiffies + char_time;
+	    schedule();
+	    if (current->signal & ~current->blocked)
+		break;
+	    if (timeout && ((orig_jiffies + timeout) < jiffies))
+		break;
 	}
+	current->state = TASK_RUNNING;
+    } else {
+	// Nothing to do!
+    }
+    /* Run one more char cycle */
+    current->state = TASK_INTERRUPTIBLE;
+    current->counter = 0;       /* make us low-priority */
+    current->timeout = jiffies + (char_time * 5);
+    schedule();
+    current->state = TASK_RUNNING;
+#ifdef CY_DEBUG_WAIT_UNTIL_SENT
+    printk("Clean (jiff=%lu)...done\n", jiffies);
+#endif
+}
 
-	info->xmit_buf[info->xmit_head++] = ch;
-	info->xmit_head &= PAGE_SIZE - 1;
-	info->xmit_cnt++;
-    restore_flags(flags);
-} /* cy_put_char */
-
-
+/*
+ * This routine is called when a particular tty device is closed.
+ */
 static void
-cy_flush_chars(struct tty_struct *tty)
+cy_close(struct tty_struct * tty, struct file * filp)
 {
-  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
   unsigned long flags;
-  unsigned char *base_addr;
-  int card,chip,channel,index;
-				
-#ifdef SERIAL_DEBUG_IO
-    printk("cy_flush_chars ttyC%d\n", info->line); /* */
+
+#ifdef CY_DEBUG_OTHER
+    printk("cyc:cy_close ttyC%d\n", info->line);
 #endif
 
-    if (serial_paranoia_check(info, tty->device, "cy_flush_chars"))
-	return;
+    if (!info
+    || serial_paranoia_check(info, tty->device, "cy_close")){
+        return;
+    }
+#ifdef CY_DEBUG_OPEN
+    printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count);
+#endif
 
-    if (info->xmit_cnt <= 0 || tty->stopped
-    || tty->hw_stopped || !info->xmit_buf)
-	return;
+    save_flags(flags); cli();
 
-    card = info->card;
-    channel = info->line - cy_card[card].first_line;
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    /* If the TTY is being hung up, nothing to do */
+    if (tty_hung_up_p(filp)) {
+        MOD_DEC_USE_COUNT;
+        restore_flags(flags);
+        return;
+    }
+        
+    if ((tty->count == 1) && (info->count != 1)) {
+        /*
+         * Uh, oh.  tty->count is 1, which means that the tty
+         * structure will be freed.  Info->count should always
+         * be one in these conditions.  If it's greater than
+         * one, we've got real problems, since it means the
+         * serial port won't be shutdown.
+         */
+        printk("cyc:cy_close: bad serial port count; tty->count is 1, "
+           "info->count is %d\n", info->count);
+        info->count = 1;
+    }
+#ifdef CY_DEBUG_COUNT
+    printk("cyc:cy_close at (%d): decrementing count to %d\n",
+        current->pid, info->count - 1);
+#endif
+    if (--info->count < 0) {
+#ifdef CY_DEBUG_COUNT
+    printk("cyc:cyc_close setting count to 0\n");
+#endif
+        info->count = 0;
+    }
+    if (info->count) {
+        MOD_DEC_USE_COUNT;
+        restore_flags(flags);
+        return;
+    }
+    info->flags |= ASYNC_CLOSING;
+    /*
+     * Save the termios structure, since this port may have
+     * separate termios for callout and dialin.
+     */
+    if (info->flags & ASYNC_NORMAL_ACTIVE)
+        info->normal_termios = *tty->termios;
+    if (info->flags & ASYNC_CALLOUT_ACTIVE)
+        info->callout_termios = *tty->termios;
 
-    save_flags(flags); cli();
-	base_addr[CyCAR<<index] = channel;
-	base_addr[CySRER<<index] |= CyTxMpty;
+    /*
+    * 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 (info->closing_wait != CY_CLOSING_WAIT_NONE)
+	tty_wait_until_sent(tty, info->closing_wait);
+
+    if (!IS_CYC_Z(cy_card[info->card])) {
+	int channel = info->line - cy_card[info->card].first_line;
+	int index = cy_card[info->card].bus_index;
+	unsigned char *base_addr = (unsigned char *)
+			(cy_card[info->card].base_addr +
+			 (cy_chip_offset[channel>>2] <<index));
+	/* Stop accepting input */
+	channel &= 0x03;
+	cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+	cy_writeb((u_long)base_addr+(CySRER<<index),
+			cy_readb(base_addr+(CySRER<<index)) & ~CyRxData);
+	if (info->flags & ASYNC_INITIALIZED) {
+	    /* Waiting for on-board buffers to be empty before closing
+	       the port */
+	    cy_wait_until_sent(tty, info->timeout);
+	}
+    } else {
+#ifdef Z_WAKE
+	/* Waiting for on-board buffers to be empty before closing the port */
+	unsigned char *base_addr = (unsigned char *) 
+					cy_card[info->card].base_addr;
+	struct FIRM_ID *firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
+	struct ZFW_CTRL *zfw_ctrl = 
+	    (struct ZFW_CTRL *) (base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	struct CH_CTRL *ch_ctrl = zfw_ctrl->ch_ctrl;
+	int channel = info->line - cy_card[info->card].first_line;
+	int retval;
+
+	if (cy_readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) {
+	    retval = cyz_issue_cmd(&cy_card[info->card], channel, 
+				   C_CM_IOCTLW, 0L);
+	    if (retval != 0){
+		printk("cyc:shutdown retval (1) was %x\n", retval);
+	    }
+	    interruptible_sleep_on(&info->shutdown_wait);
+	}
+#endif
+    }
+
+    shutdown(info);
+    if (tty->driver.flush_buffer)
+        tty->driver.flush_buffer(tty);
+    if (tty->ldisc.flush_buffer)
+        tty->ldisc.flush_buffer(tty);
+    tty->closing = 0;
+    info->event = 0;
+    info->tty = 0;
+    if (info->blocked_open) {
+        if (info->close_delay) {
+            current->state = TASK_INTERRUPTIBLE;
+            current->timeout = jiffies + info->close_delay;
+            schedule();
+        }
+        wake_up_interruptible(&info->open_wait);
+    }
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+                     ASYNC_CLOSING);
+    wake_up_interruptible(&info->close_wait);
+
+#ifdef CY_DEBUG_OTHER
+    printk(" cyc:cy_close done\n");
+#endif
+
+    MOD_DEC_USE_COUNT;
     restore_flags(flags);
-} /* cy_flush_chars */
+} /* cy_close */
 
 
 /* This routine gets called when tty_write has put something into
-    the write_queue.  If the port is not already transmitting stuff,
-    start it off by enabling interrupts.  The interrupt service
-    routine will then ensure that the characters are sent.  If the
-    port is already active, there is no need to kick it.
+ * the write_queue.  The characters may come from user space or
+ * kernel space.
+ *
+ * This routine will return the number of characters actually
+ * accepted for writing.
+ *
+ * If the port is not already transmitting stuff, start it off by
+ * enabling interrupts.  The interrupt service routine will then
+ * ensure that the characters are sent.
+ * If the port is already active, there is no need to kick it.
+ *
  */
 static int
 cy_write(struct tty_struct * tty, int from_user,
@@ -1643,197 +2833,633 @@
   unsigned long flags;
   int c, total = 0;
 
-#ifdef SERIAL_DEBUG_IO
-    printk("cy_write ttyC%d\n", info->line); /* */
+#ifdef CY_DEBUG_IO
+    printk("cyc:cy_write ttyC%d\n", info->line); /* */
 #endif
 
     if (serial_paranoia_check(info, tty->device, "cy_write")){
-	return 0;
+        return 0;
     }
-	
+        
     if (!tty || !info->xmit_buf || !tmp_buf){
         return 0;
     }
 
+    if (from_user)
+	down(&tmp_buf_sem);
+    save_flags(flags);               
     while (1) {
-        save_flags(flags); cli();		
-	c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-			   SERIAL_XMIT_SIZE - info->xmit_head));
-	if (c <= 0){
-	    restore_flags(flags);
-	    break;
-	}
+        cli();               
+        c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                           SERIAL_XMIT_SIZE - info->xmit_head));
+        if (c <= 0)
+            break;
 
-	if (from_user) {
-	    down(&tmp_buf_sem);
-	    memcpy_fromfs(tmp_buf, buf, c);
-	    c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-		       SERIAL_XMIT_SIZE - info->xmit_head));
-	    memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
-	    up(&tmp_buf_sem);
-	} else
-	    memcpy(info->xmit_buf + info->xmit_head, buf, c);
-	info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-	info->xmit_cnt += c;
-	restore_flags(flags);
-	buf += c;
-	count -= c;
-	total += c;
+        if (from_user) {
+            copy_from_user(tmp_buf, buf, c);
+            c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                       SERIAL_XMIT_SIZE - info->xmit_head));
+            memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+        } else
+            memcpy(info->xmit_buf + info->xmit_head, buf, c);
+        info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+        info->xmit_cnt += c;
+        restore_flags(flags);
+        buf += c;
+        count -= c;
+        total += c;
+#if 0
+        SP("CW");
+	CP16(c);
+	SP(" ");
+#endif
     }
 
-
-    if (info->xmit_cnt
-    && !tty->stopped
-    && !tty->hw_stopped ) {
+    info->idle_stats.xmit_bytes += total;
+    info->idle_stats.xmit_idle   = jiffies;
+
+    if (from_user)
+	up(&tmp_buf_sem);
+    if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
         start_xmit(info);
     }
+    restore_flags(flags);
     return total;
 } /* cy_write */
 
 
-static int
-cy_write_room(struct tty_struct *tty)
+/*
+ * This routine is called by the kernel to write a single
+ * character to the tty device.  If the kernel uses this routine,
+ * it must call the flush_chars() routine (if defined) when it is
+ * done stuffing characters into the driver.  If there is no room
+ * in the queue, the character is ignored.
+ */
+static void
+cy_put_char(struct tty_struct *tty, unsigned char ch)
 {
   struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
-  int	ret;
-				
-#ifdef SERIAL_DEBUG_IO
-    printk("cy_write_room ttyC%d\n", info->line); /* */
+  unsigned long flags;
+
+#ifdef CY_DEBUG_IO
+    printk("cyc:cy_put_char ttyC%d\n", info->line);
 #endif
 
-    if (serial_paranoia_check(info, tty->device, "cy_write_room"))
-	return 0;
-    ret = PAGE_SIZE - info->xmit_cnt - 1;
-    if (ret < 0)
-	ret = 0;
-    return ret;
-} /* cy_write_room */
+    if (serial_paranoia_check(info, tty->device, "cy_put_char"))
+        return;
 
+    if (!tty || !info->xmit_buf)
+        return;
 
-static int
-cy_chars_in_buffer(struct tty_struct *tty)
-{
-  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
-				
-#ifdef SERIAL_DEBUG_IO
-    printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */
+    save_flags(flags); cli();
+        if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+            restore_flags(flags);
+            return;
+        }
+
+        info->xmit_buf[info->xmit_head++] = ch;
+        info->xmit_head &= SERIAL_XMIT_SIZE - 1;
+        info->xmit_cnt++;
+	info->idle_stats.xmit_bytes++;
+	info->idle_stats.xmit_idle = jiffies;
+    restore_flags(flags);
+#if 0
+	SP("+");
 #endif
-
-    if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer"))
-	return 0;
-
-    return info->xmit_cnt;
-} /* cy_chars_in_buffer */
+} /* cy_put_char */
 
 
+/*
+ * This routine is called by the kernel after it has written a
+ * series of characters to the tty device using put_char().  
+ */
 static void
-cy_flush_buffer(struct tty_struct *tty)
+cy_flush_chars(struct tty_struct *tty)
 {
   struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
   unsigned long flags;
-				
-#ifdef SERIAL_DEBUG_IO
-    printk("cy_flush_buffer ttyC%d\n", info->line); /* */
+  unsigned char *base_addr;
+  int card,chip,channel,index;
+                                
+#ifdef CY_DEBUG_IO
+    printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */
 #endif
 
-    if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
-	return;
-    save_flags(flags); cli();
-	info->xmit_cnt = info->xmit_head = info->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);
-} /* cy_flush_buffer */
+    if (serial_paranoia_check(info, tty->device, "cy_flush_chars"))
+        return;
 
+    if (info->xmit_cnt <= 0 || tty->stopped
+    || tty->hw_stopped || !info->xmit_buf)
+        return;
 
-/* This routine is called by the upper-layer tty layer to signal
-   that incoming characters should be throttled or that the
-   throttle should be released.
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+	save_flags(flags); cli();
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+	    cy_writeb((u_long)base_addr+(CySRER<<index),
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
+	restore_flags(flags);
+    } else {
+	/* Since polling is already in place,
+	    nothing further need be done.  */
+    }
+} /* cy_flush_chars */
+
+
+/*
+ * This routine returns the numbers of characters the tty driver
+ * will accept for queuing to be written.  This number is subject
+ * to change as output buffers get emptied, or if the output flow
+ * control is activated.
  */
-static void
-cy_throttle(struct tty_struct * tty)
+static int
+cy_write_room(struct tty_struct *tty)
 {
   struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
-  unsigned long flags;
-  unsigned char *base_addr;
-  int card,chip,channel,index;
-
-#ifdef SERIAL_DEBUG_THROTTLE
-  char buf[64];
-	
-    printk("throttle %s: %d....\n", _tty_name(tty, buf),
-	   tty->ldisc.chars_in_buffer(tty));
-    printk("cy_throttle ttyC%d\n", info->line);
+  int   ret;
+                                
+#ifdef CY_DEBUG_IO
+    printk("cyc:cy_write_room ttyC%d\n", info->line); /* */
 #endif
 
-    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
-	    return;
-    }
+    if (serial_paranoia_check(info, tty->device, "cy_write_room"))
+        return 0;
+    ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+    if (ret < 0)
+        ret = 0;
+    return ret;
+} /* cy_write_room */
 
-    if (I_IXOFF(tty)) {
-	info->x_char = STOP_CHAR(tty);
-	    /* Should use the "Send Special Character" feature!!! */
-    }
+
+static int
+cy_chars_in_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  int card, channel;
+                                
+    if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer"))
+        return 0;
 
     card = info->card;
-    channel = info->line - cy_card[card].first_line;
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    channel = (info->line) - (cy_card[card].first_line);
 
-    save_flags(flags); cli();
-	base_addr[CyCAR<<index] = (u_char)channel;
-	base_addr[CyMSVR1<<index] = ~CyRTS;
-    restore_flags(flags);
+    if (!IS_CYC_Z(cy_card[card])) {
+#ifdef CY_DEBUG_IO
+	printk("cyc:cy_chars_in_buffer ttyC%d %d\n",
+		info->line, info->xmit_cnt); /* */
+#endif
+	return info->xmit_cnt;
+    } else {
+	static volatile struct FIRM_ID *firm_id;
+	static volatile struct ZFW_CTRL *zfw_ctrl;
+	static volatile struct CH_CTRL *ch_ctrl;
+	static volatile struct BUF_CTRL *buf_ctrl;
+	int char_count;
+	volatile uclong tx_put, tx_get, tx_bufsize;
+
+	firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS);
+	zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr +
+				cy_readl(&firm_id->zfwctrl_addr));
+	ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+	buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
+
+	tx_get = cy_readl(&buf_ctrl->tx_get);
+	tx_put = cy_readl(&buf_ctrl->tx_put);
+	tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+	if (tx_put >= tx_get)
+	    char_count = tx_put - tx_get;
+	else
+	    char_count = tx_put - tx_get + tx_bufsize;
+#ifdef CY_DEBUG_IO
+	printk("cyc:cy_chars_in_buffer ttyC%d %d\n",
+		info->line, info->xmit_cnt + char_count); /* */
+#endif
+	return (info->xmit_cnt + char_count);
+    }
+} /* cy_chars_in_buffer */
 
-    return;
-} /* cy_throttle */
+
+/*
+ * ------------------------------------------------------------
+ * cy_ioctl() and friends
+ * ------------------------------------------------------------
+ */
 
 
+/*
+ * This routine finds or computes the various line characteristics.
+ * It used to be called config_setup
+ */
 static void
-cy_unthrottle(struct tty_struct * tty)
+set_line_char(struct cyclades_port * info)
 {
-  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
   unsigned long flags;
   unsigned char *base_addr;
   int card,chip,channel,index;
+  unsigned cflag, iflag;
+  unsigned short chip_number;
+  int   i;
 
-#ifdef SERIAL_DEBUG_THROTTLE
-  char buf[64];
-	
-    printk("throttle %s: %d....\n", _tty_name(tty, buf),
-	   tty->ldisc.chars_in_buffer(tty));
-    printk("cy_unthrottle ttyC%d\n", info->line);
-#endif
 
-    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
-	    return;
+    if (!info->tty || !info->tty->termios){
+        return;
     }
-
-    if (I_IXOFF(tty)) {
-	info->x_char = START_CHAR(tty);
-	/* Should use the "Send Special Character" feature!!! */
+    if (info->line == -1){
+        return;
     }
+    cflag = info->tty->termios->c_cflag;
+    iflag = info->tty->termios->c_iflag;
 
     card = info->card;
-    channel = info->line - cy_card[card].first_line;
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    channel = (info->line) - (cy_card[card].first_line);
+    chip_number = channel / 4;
 
-    save_flags(flags); cli();
-	base_addr[CyCAR<<index] = (u_char)channel;
-	base_addr[CyMSVR1<<index] = CyRTS;
-    restore_flags(flags);
+    if (!IS_CYC_Z(cy_card[card])) {
+
+	index = cy_card[card].bus_index;
+
+	/* baud rate */
+	i = cflag & CBAUD;
+     
+	if (i & CBAUDEX) {
+	    if (i == B57600)
+		i = 16;
+#ifdef B76800
+	    else if(i == B76800) 
+		i = 17;
+#endif
+	    else if(i == B115200) 
+		i = 18;
+	    else if(i == B230400 && (info->chip_rev >= CD1400_REV_J)) {
+		/* It is a CD1400 rev. J or later */
+		i = 20;
+	    }
+	    else
+		info->tty->termios->c_cflag &= ~CBAUDEX;
+	}
+
+	if (i == 15) {
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+		i += 1;
+	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+		i += 3;
+       	    if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+                switch(info->baud) {
+		    case 57600:
+			i += 1; break;
+#ifdef B76800
+		    case 76800: 
+			i += 2; break;
+#endif
+		    case 115200:
+			i += 3; break;
+		    case 230400:
+			i += 5; break;
+		    default:
+			break;
+	        }
+            }
+	}
+	if(info->chip_rev >= CD1400_REV_J) {
+	    /* It is a CD1400 rev. J or later */
+	    info->tbpr = baud_bpr_60[i]; /* Tx BPR */
+	    info->tco = baud_co_60[i]; /* Tx CO */
+	    info->rbpr = baud_bpr_60[i]; /* Rx BPR */
+	    info->rco = baud_co_60[i]; /* Rx CO */
+	} else {
+	    info->tbpr = baud_bpr_25[i]; /* Tx BPR */
+	    info->tco = baud_co_25[i]; /* Tx CO */
+	    info->rbpr = baud_bpr_25[i]; /* Rx BPR */
+	    info->rco = baud_co_25[i]; /* Rx CO */
+	}
+	if (baud_table[i] == 134) {
+	    info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+	    /* get it right for 134.5 baud */
+	} else if (baud_table[i]) {
+	    info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
+	    /* this needs to be propagated into the card info */
+	} else {
+	    info->timeout = 0;
+	}
+	/* By tradition (is it a standard?) a baud rate of zero
+	   implies the line should be/has been closed.  A bit
+	   later in this routine such a test is performed. */
+
+	/* byte size and parity */
+	info->cor5 = 0;
+	info->cor4 = 0;
+	info->cor3 = (info->default_threshold
+		      ? info->default_threshold
+		      : baud_cor3[i]); /* receive threshold */
+	info->cor2 = CyETC;
+	switch(cflag & CSIZE){
+	case CS5:
+	    info->cor1 = Cy_5_BITS;
+	    break;
+	case CS6:
+	    info->cor1 = Cy_6_BITS;
+	    break;
+	case CS7:
+	    info->cor1 = Cy_7_BITS;
+	    break;
+	case CS8:
+	    info->cor1 = Cy_8_BITS;
+	    break;
+	}
+	if(cflag & CSTOPB){
+	    info->cor1 |= Cy_2_STOP;
+	}
+	if (cflag & PARENB){
+	    if (cflag & PARODD){
+		info->cor1 |= CyPARITY_O;
+	    }else{
+		info->cor1 |= CyPARITY_E;
+	    }
+	}else{
+	    info->cor1 |= CyPARITY_NONE;
+	}
+	    
+	/* CTS flow control flag */
+	if (cflag & CRTSCTS){
+	    info->flags |= ASYNC_CTS_FLOW;
+	    info->cor2 |= CyCtsAE;
+	}else{
+	    info->flags &= ~ASYNC_CTS_FLOW;
+	    info->cor2 &= ~CyCtsAE;
+	}
+	if (cflag & CLOCAL)
+	    info->flags &= ~ASYNC_CHECK_CD;
+	else
+	    info->flags |= ASYNC_CHECK_CD;
+
+	 /***********************************************
+	    The hardware option, CyRtsAO, presents RTS when
+	    the chip has characters to send.  Since most modems
+	    use RTS as reverse (inbound) flow control, this
+	    option is not used.  If inbound flow control is
+	    necessary, DTR can be programmed to provide the
+	    appropriate signals for use with a non-standard
+	    cable.  Contact Marcio Saito for details.
+	 ***********************************************/
+
+	chip = channel>>2;
+	channel &= 0x03;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+	save_flags(flags); cli();
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+
+	   /* tx and rx baud rate */
+
+	    cy_writeb((u_long)base_addr+(CyTCOR<<index), info->tco);
+	    cy_writeb((u_long)base_addr+(CyTBPR<<index), info->tbpr);
+	    cy_writeb((u_long)base_addr+(CyRCOR<<index), info->rco);
+	    cy_writeb((u_long)base_addr+(CyRBPR<<index), info->rbpr);
+
+	    /* set line characteristics  according configuration */
+
+	    cy_writeb((u_long)base_addr+(CySCHR1<<index), 
+		      START_CHAR(info->tty));
+	    cy_writeb((u_long)base_addr+(CySCHR2<<index), 
+		      STOP_CHAR(info->tty));
+	    cy_writeb((u_long)base_addr+(CyCOR1<<index), info->cor1);
+	    cy_writeb((u_long)base_addr+(CyCOR2<<index), info->cor2);
+	    cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
+	    cy_writeb((u_long)base_addr+(CyCOR4<<index), info->cor4);
+	    cy_writeb((u_long)base_addr+(CyCOR5<<index), info->cor5);
+
+	    cyy_issue_cmd(base_addr,
+		     CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index);
+
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), 
+		      (u_char)channel); /* !!! Is this needed? */
+	    cy_writeb((u_long)base_addr+(CyRTPR<<index), (info->default_timeout
+					         ? info->default_timeout
+					         : 0x02)); /* 10ms rx timeout */
+
+	    if (C_CLOCAL(info->tty)) {
+		/* without modem intr */
+		cy_writeb((u_long)base_addr+(CySRER<<index),
+                   cy_readb(base_addr+(CySRER<<index)) | CyMdmCh); 
+					/* act on 1->0 modem transitions */
+                if ((cflag & CRTSCTS) && info->rflow) {
+                        cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+                                  (CyCTS|rflow_thr[i]));
+                } else {
+                        cy_writeb((u_long)base_addr+(CyMCOR1<<index), CyCTS);
+                }
+					/* act on 0->1 modem transitions */
+		cy_writeb((u_long)base_addr+(CyMCOR2<<index), CyCTS);
+	    } else {
+		/* without modem intr */
+		cy_writeb((u_long)base_addr+(CySRER<<index),
+                   cy_readb(base_addr+(CySRER<<index)) | CyMdmCh); 
+					/* act on 1->0 modem transitions */
+                if ((cflag & CRTSCTS) && info->rflow) {
+			cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+        	                  (CyDSR|CyCTS|CyRI|CyDCD|rflow_thr[i]));
+                } else {
+			cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+                                  CyDSR|CyCTS|CyRI|CyDCD);
+                }
+					/* act on 0->1 modem transitions */
+		cy_writeb((u_long)base_addr+(CyMCOR2<<index), 
+			  CyDSR|CyCTS|CyRI|CyDCD);
+	    }
+
+	    if(i == 0){ /* baud rate is zero, turn off line */
+	        if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+		} else {
+                        cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+		}
+#ifdef CY_DEBUG_DTR
+		printk("cyc:set_line_char dropping DTR\n");
+		printk("     status: 0x%x,
+		    0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)),
+		    cy_readb(base_addr+(CyMSVR2<<index)));
+#endif
+	    }else{
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
+#ifdef CY_DEBUG_DTR
+		printk("cyc:set_line_char raising DTR\n");
+		printk("     status: 0x%x, 0x%x\n",
+		    cy_readb(base_addr+(CyMSVR1<<index)),
+		    cy_readb(base_addr+(CyMSVR2<<index)));
+#endif
+	    }
+
+	    if (info->tty){
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	    }
+
+	restore_flags(flags);
+    } else {
+      struct FIRM_ID *firm_id;
+      struct ZFW_CTRL *zfw_ctrl;
+      struct BOARD_CTRL *board_ctrl;
+      struct CH_CTRL *ch_ctrl;
+      struct BUF_CTRL *buf_ctrl;
+      int retval;
+
+        firm_id = (struct FIRM_ID *)
+			(cy_card[card].base_addr + ID_ADDRESS);
+        if (!ISZLOADED(cy_card[card])) {
+	    return;
+	}
+
+	zfw_ctrl = (struct ZFW_CTRL *)
+                (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	board_ctrl = &zfw_ctrl->board_ctrl;
+	ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+	buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
+
+	/* baud rate */
+	switch(i = cflag & CBAUD){
+	/*
+        case B0: cy_writel(&ch_ctrl->comm_baud , 0); break;
+	*/
+        case B50: cy_writel(&ch_ctrl->comm_baud , 50); break;
+        case B75: cy_writel(&ch_ctrl->comm_baud , 75); break;
+        case B110: cy_writel(&ch_ctrl->comm_baud , 110); break;
+        case B134: cy_writel(&ch_ctrl->comm_baud , 134); break;
+        case B150: cy_writel(&ch_ctrl->comm_baud , 150); break;
+        case B200: cy_writel(&ch_ctrl->comm_baud , 200); break;
+        case B300: cy_writel(&ch_ctrl->comm_baud , 300); break;
+        case B600: cy_writel(&ch_ctrl->comm_baud , 600); break;
+        case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break;
+        case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break;
+        case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break;
+        case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break;
+        case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break;
+        case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break;
+        case B38400:
+            if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){
+                cy_writel(&ch_ctrl->comm_baud , 57600);
+            }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){
+                cy_writel(&ch_ctrl->comm_baud , 115200);
+            }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+                cy_writel(&ch_ctrl->comm_baud , info->baud);
+            }else{
+                cy_writel(&ch_ctrl->comm_baud , 38400);
+            }
+            break;
+        case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break;
+#ifdef B76800
+        case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break;
+#endif
+        case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break;
+        case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break;
+        case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break;
+	}
+
+	if ((i = cy_readl(&ch_ctrl->comm_baud)) == 134) {
+	    info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+	    /* get it right for 134.5 baud */
+	} else if (i) {
+	    info->timeout = (info->xmit_fifo_size*HZ*15/i) + 2;
+	    /* this needs to be propagated into the card info */
+	} else {
+	    info->timeout = 0;
+	}
+
+	/* byte size and parity */
+	switch(cflag & CSIZE){
+	case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break;
+	case CS6: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS6); break;
+	case CS7: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS7); break;
+	case CS8: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS8); break;
+	}
+	if(cflag & CSTOPB){
+	    cy_writel(&ch_ctrl->comm_data_l,
+               cy_readl(&ch_ctrl->comm_data_l) | C_DL_2STOP);
+	}else{
+	    cy_writel(&ch_ctrl->comm_data_l,
+               cy_readl(&ch_ctrl->comm_data_l) | C_DL_1STOP);
+	}
+	if (cflag & PARENB){
+	    if (cflag & PARODD){
+		cy_writel(&ch_ctrl->comm_parity , C_PR_ODD);
+	    }else{
+		cy_writel(&ch_ctrl->comm_parity , C_PR_EVEN);
+	    }
+	}else{
+	    cy_writel(&ch_ctrl->comm_parity , C_PR_NONE);
+	}
+
+	/* CTS flow control flag */
+	if (cflag & CRTSCTS){
+	    info->flags |= ASYNC_CTS_FLOW;
+	    cy_writel(&ch_ctrl->hw_flow,
+               cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
+	}else{
+	    info->flags &= ~ASYNC_CTS_FLOW;
+	    cy_writel(&ch_ctrl->hw_flow,
+               cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS));
+	}
+
+	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L);
+	if (retval != 0){
+	    printk("cyc:set_line_char retval at %d was %x\n",
+	        __LINE__, retval);
+	}
+
+	/* CD sensitivity */
+	if (cflag & CLOCAL){
+	    info->flags &= ~ASYNC_CHECK_CD;
+	}else{
+	    info->flags |= ASYNC_CHECK_CD;
+	}
+
+	if (iflag & IXON){
+	    cy_writel(&ch_ctrl->sw_flow, 
+		cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX);
+	} else {
+	    cy_writel(&ch_ctrl->sw_flow, 
+		cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX);
+	}
+
+	if(i == 0){ /* baud rate is zero, turn off line */
+	    cy_writel(&ch_ctrl->rs_control,
+               cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+	    printk("cyc:set_line_char dropping Z DTR\n");
+#endif
+	}else{
+	    cy_writel(&ch_ctrl->rs_control,
+               cy_readl(&ch_ctrl->rs_control) | C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+	    printk("cyc:set_line_char raising Z DTR\n");
+#endif
+	}
+
+	retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTLM, 0L);
+	if (retval != 0){
+	    printk("cyc:set_line_char retval at %d was %x\n",
+	        __LINE__, retval);
+	}
+
+	if (info->tty){
+	    clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	}
+    }
+
+} /* set_line_char */
 
-    return;
-} /* cy_unthrottle */
 
 static int
 get_serial_info(struct cyclades_port * info,
@@ -1850,14 +3476,15 @@
     tmp.port = info->card * 0x100 + info->line - cinfo->first_line;
     tmp.irq = cinfo->irq;
     tmp.flags = info->flags;
-    tmp.baud_base = 0;          /*!!!*/
     tmp.close_delay = info->close_delay;
+    tmp.baud_base = info->baud;
     tmp.custom_divisor = 0;     /*!!!*/
     tmp.hub6 = 0;               /*!!!*/
-    memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+    copy_to_user(retinfo,&tmp,sizeof(*retinfo));
     return 0;
 } /* get_serial_info */
 
+
 static int
 set_serial_info(struct cyclades_port * info,
                            struct serial_struct * new_info)
@@ -1866,18 +3493,20 @@
   struct cyclades_port old_info;
 
     if (!new_info)
-	    return -EFAULT;
-    memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+            return -EFAULT;
+    copy_from_user(&new_serial,new_info,sizeof(new_serial));
     old_info = *info;
 
     if (!suser()) {
-	    if ((new_serial.close_delay != info->close_delay) ||
+            if ((new_serial.close_delay != info->close_delay) ||
+		(new_serial.baud_base != info->baud) ||
 		((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
 		 (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
-		    return -EPERM;
-	    info->flags = ((info->flags & ~ASYNC_USR_MASK) |
-			   (new_serial.flags & ASYNC_USR_MASK));
-	    goto check_and_exit;
+                    return -EPERM;
+            info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+                           (new_serial.flags & ASYNC_USR_MASK));
+            info->baud = new_serial.baud_base;
+            goto check_and_exit;
     }
 
 
@@ -1886,20 +3515,23 @@
      * At this point, we start making changes.....
      */
 
+    info->baud = new_serial.baud_base;
     info->flags = ((info->flags & ~ASYNC_FLAGS) |
-		    (new_serial.flags & ASYNC_FLAGS));
-    info->close_delay = new_serial.close_delay;
-
+                    (new_serial.flags & ASYNC_FLAGS));
+    info->close_delay = new_serial.close_delay * HZ/100;
+    info->closing_wait = new_serial.closing_wait * HZ/100;
+ 
 
 check_and_exit:
     if (info->flags & ASYNC_INITIALIZED){
-	config_setup(info);
-	return 0;
+        set_line_char(info);
+        return 0;
     }else{
         return startup(info);
     }
 } /* set_serial_info */
 
+
 static int
 get_modem_info(struct cyclades_port * info, unsigned int *value)
 {
@@ -1907,32 +3539,73 @@
   unsigned char *base_addr;
   unsigned long flags;
   unsigned char status;
+  unsigned long lstatus;
   unsigned int result;
+  struct FIRM_ID *firm_id;
+  struct ZFW_CTRL *zfw_ctrl;
+  struct BOARD_CTRL *board_ctrl;
+  struct CH_CTRL *ch_ctrl;
 
     card = info->card;
     channel = (info->line) - (cy_card[card].first_line);
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-                   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
 
-    save_flags(flags); cli();
-        base_addr[CyCAR<<index] = (u_char)channel;
-        status = base_addr[CyMSVR1<<index];
-	status |= base_addr[CyMSVR2<<index];
-    restore_flags(flags);
+	save_flags(flags); cli();
+	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+	    status = cy_readb(base_addr+(CyMSVR1<<index));
+	    status |= cy_readb(base_addr+(CyMSVR2<<index));
+	restore_flags(flags);
+
+
+        if (info->rtsdtr_inv) {
+	    result =  ((status  & CyRTS) ? TIOCM_DTR : 0)
+		    | ((status  & CyDTR) ? TIOCM_RTS : 0);
+	} else {
+	    result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
+		    | ((status  & CyDTR) ? TIOCM_DTR : 0);
+	}
+	result |=  ((status  & CyDCD) ? TIOCM_CAR : 0)
+		 | ((status  & CyRI) ? TIOCM_RNG : 0)
+		 | ((status  & CyDSR) ? TIOCM_DSR : 0)
+		 | ((status  & CyCTS) ? TIOCM_CTS : 0);
+    } else {
+	base_addr = (unsigned char*) (cy_card[card].base_addr);
+
+        if (cy_card[card].num_chips != -1){
+	    return -EINVAL;
+	}
+
+	firm_id = (struct FIRM_ID *)
+		    (cy_card[card].base_addr + ID_ADDRESS);
+        if (ISZLOADED(cy_card[card])) {
+	    zfw_ctrl = (struct ZFW_CTRL *)
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	    board_ctrl = &zfw_ctrl->board_ctrl;
+	    ch_ctrl = zfw_ctrl->ch_ctrl;
+	    lstatus = cy_readl(&ch_ctrl[channel].rs_status);
+	    result =  ((lstatus  & C_RS_RTS) ? TIOCM_RTS : 0)
+		    | ((lstatus  & C_RS_DTR) ? TIOCM_DTR : 0)
+	            | ((lstatus  & C_RS_DCD) ? TIOCM_CAR : 0)
+		    | ((lstatus  & C_RS_RI) ? TIOCM_RNG : 0)
+		    | ((lstatus  & C_RS_DSR) ? TIOCM_DSR : 0)
+		    | ((lstatus  & C_RS_CTS) ? TIOCM_CTS : 0);
+	}else{
+	    result = 0;
+	    return -ENODEV;
+	}
 
-    result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
-            | ((status  & CyDTR) ? TIOCM_DTR : 0)
-            | ((status  & CyDCD) ? TIOCM_CAR : 0)
-            | ((status  & CyRI) ? TIOCM_RNG : 0)
-            | ((status  & CyDSR) ? TIOCM_DSR : 0)
-            | ((status  & CyCTS) ? TIOCM_CTS : 0);
-    put_fs_long(result,(unsigned long *) value);
+    }
+    cy_put_user(result,(unsigned long *) value);
     return 0;
 } /* get_modem_info */
 
+
 static int
 set_modem_info(struct cyclades_port * info, unsigned int cmd,
                           unsigned int *value)
@@ -1940,221 +3613,398 @@
   int card,chip,channel,index;
   unsigned char *base_addr;
   unsigned long flags;
-  unsigned int arg = get_fs_long((unsigned long *) value);
+  unsigned int arg = cy_get_user((unsigned long *) value);
+  struct FIRM_ID *firm_id;
+  struct ZFW_CTRL *zfw_ctrl;
+  struct BOARD_CTRL *board_ctrl;
+  struct CH_CTRL *ch_ctrl;
+  int retval;
 
     card = info->card;
     channel = (info->line) - (cy_card[card].first_line);
-    chip = channel>>2;
-    channel &= 0x03;
-    index = cy_card[card].bus_index;
-    base_addr = (unsigned char*)
-                   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
 
-    switch (cmd) {
-    case TIOCMBIS:
-	if (arg & TIOCM_RTS){
-	    save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR1<<index] = CyRTS;
-	    restore_flags(flags);
-	}
-	if (arg & TIOCM_DTR){
-	    save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    base_addr[CyMSVR2<<index] = CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-            printk("cyc: %d: raising DTR\n", __LINE__);
-            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+	switch (cmd) {
+	case TIOCMBIS:
+	    if (arg & TIOCM_RTS){
+		save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+		    cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                } else {
+		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                }
+		restore_flags(flags);
+	    }
+	    if (arg & TIOCM_DTR){
+		save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+		    cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
+#ifdef CY_DEBUG_DTR
+		printk("cyc:set_modem_info raising DTR\n");
+		printk("     status: 0x%x, 0x%x\n",
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-	    restore_flags(flags);
-	}
-	break;
-    case TIOCMBIC:
-	if (arg & TIOCM_RTS){
-	    save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR1<<index] = ~CyRTS;
-	    restore_flags(flags);
-	}
-	if (arg & TIOCM_DTR){
-	    save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    base_addr[CyMSVR2<<index] = ~CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-            printk("cyc: %d: dropping DTR\n", __LINE__);
-            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		restore_flags(flags);
+	    }
+	    break;
+	case TIOCMBIC:
+	    if (arg & TIOCM_RTS){
+		save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), 
+                          (u_char)channel);
+                if (info->rtsdtr_inv) {
+		    	cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                } else {
+		    	cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                }
+		restore_flags(flags);
+	    }
+	    if (arg & TIOCM_DTR){
+		save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                }
+#ifdef CY_DEBUG_DTR
+		printk("cyc:set_modem_info dropping DTR\n");
+		printk("     status: 0x%x, 0x%x\n",
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-	    restore_flags(flags);
-	}
-	break;
-    case TIOCMSET:
-	if (arg & TIOCM_RTS){
-	    save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR1<<index] = CyRTS;
-	    restore_flags(flags);
-	}else{
-	    save_flags(flags); cli();
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR1<<index] = ~CyRTS;
-	    restore_flags(flags);
-	}
-	if (arg & TIOCM_DTR){
-	    save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    base_addr[CyMSVR2<<index] = CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-            printk("cyc: %d: raising DTR\n", __LINE__);
-            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		restore_flags(flags);
+	    }
+	    break;
+	case TIOCMSET:
+	    if (arg & TIOCM_RTS){
+		save_flags(flags); cli();
+	        cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                }
+		restore_flags(flags);
+	    }else{
+		save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                }
+		restore_flags(flags);
+	    }
+	    if (arg & TIOCM_DTR){
+		save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
+#ifdef CY_DEBUG_DTR
+		printk("cyc:set_modem_info raising DTR\n");
+		printk("     status: 0x%x, 0x%x\n",
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-	    restore_flags(flags);
-	}else{
-	    save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    base_addr[CyMSVR2<<index] = ~CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-            printk("cyc: %d: dropping DTR\n", __LINE__);
-            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+		restore_flags(flags);
+	    }else{
+		save_flags(flags); cli();
+		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                } else {
+			cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                }
+
+#ifdef CY_DEBUG_DTR
+		printk("cyc:set_modem_info dropping DTR\n");
+		printk("     status: 0x%x, 0x%x\n",
+		    cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-	    restore_flags(flags);
+		restore_flags(flags);
+	    }
+	    break;
+	default:
+	    return -EINVAL;
 	}
-	break;
-    default:
+    } else {
+	base_addr = (unsigned char*) (cy_card[card].base_addr);
+
+	firm_id = (struct FIRM_ID *)
+		    (cy_card[card].base_addr + ID_ADDRESS);
+        if (ISZLOADED(cy_card[card])) {
+	    zfw_ctrl = (struct ZFW_CTRL *)
+		(cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+	    board_ctrl = &zfw_ctrl->board_ctrl;
+	    ch_ctrl = zfw_ctrl->ch_ctrl;
+
+	    switch (cmd) {
+	    case TIOCMBIS:
+		if (arg & TIOCM_RTS){
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
+		}
+		if (arg & TIOCM_DTR){
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+		    printk("cyc:set_modem_info raising Z DTR\n");
+#endif
+		}
+		break;
+	    case TIOCMBIC:
+		if (arg & TIOCM_RTS){
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
+		}
+		if (arg & TIOCM_DTR){
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+		    printk("cyc:set_modem_info clearing Z DTR\n");
+#endif
+		}
+		break;
+	    case TIOCMSET:
+		if (arg & TIOCM_RTS){
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
+		}else{
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
+		}
+		if (arg & TIOCM_DTR){
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+		    printk("cyc:set_modem_info raising Z DTR\n");
+#endif
+		}else{
+		    cy_writel(&ch_ctrl[channel].rs_control,
+                       cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
+#ifdef CY_DEBUG_DTR
+		    printk("cyc:set_modem_info clearing Z DTR\n");
+#endif
+		}
+		break;
+	    default:
 		return -EINVAL;
-        }
+	    }
+	}else{
+	    return -ENODEV;
+	}
+        retval = cyz_issue_cmd(&cy_card[info->card],
+				    channel, C_CM_IOCTLM,0L);
+	if (retval != 0){
+	    printk("cyc:set_modem_info retval at %d was %x\n",
+	        __LINE__, retval);
+	}
+    }
     return 0;
 } /* set_modem_info */
 
+
 static void
 send_break( struct cyclades_port * info, int duration)
-{ /* Let the transmit ISR take care of this (since it
-     requires stuffing characters into the output stream).
-   */
-    info->x_break = duration;
-    if (!info->xmit_cnt ) {
-	start_xmit(info);
+{
+
+    if (!IS_CYC_Z(cy_card[info->card])) {
+        /* Let the transmit ISR take care of this (since it
+	   requires stuffing characters into the output stream).
+        */
+	info->x_break = duration;
+	if (!info->xmit_cnt ) {
+	    start_xmit(info);
+	}
+    } else {
+	/* For the moment we ignore the duration parameter!!!
+	   A better implementation will use C_CM_SET_BREAK
+	   and C_CM_CLR_BREAK with the appropriate delay.
+	 */
+#if 1
+// this appears to wedge the output data stream
+int retval;
+        retval = cyz_issue_cmd(&cy_card[info->card],
+		(info->line) - (cy_card[info->card].first_line),
+		C_CM_SENDBRK, 0L);
+	if (retval != 0){
+	    printk("cyc:send_break retval at %d was %x\n",
+	        __LINE__, retval);
+	}
+#endif
     }
 } /* send_break */
 
+
 static int
 get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
 {
 
-   memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor));
-   info->mon.int_count  = 0;
-   info->mon.char_count = 0;
-   info->mon.char_max   = 0;
-   info->mon.char_last  = 0;
-   return 0;
-}
+    copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor));
+    info->mon.int_count  = 0;
+    info->mon.char_count = 0;
+    info->mon.char_max   = 0;
+    info->mon.char_last  = 0;
+    return 0;
+}/* get_mon_info */
+
 
 static int
 set_threshold(struct cyclades_port * info, unsigned long value)
 {
-   unsigned char *base_addr;
-   int card,channel,chip,index;
+  unsigned char *base_addr;
+  int card,channel,chip,index;
    
-   card = info->card;
-   channel = info->line - cy_card[card].first_line;
-   chip = channel>>2;
-   channel &= 0x03;
-   index = cy_card[card].bus_index;
-   base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+	info->cor3 &= ~CyREC_FIFO;
+	info->cor3 |= value & CyREC_FIFO;
+	cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
+	cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
+    } else {
+	// Nothing to do!
+    }
+    return 0;
+}/* set_threshold */
 
-   info->cor3 &= ~CyREC_FIFO;
-   info->cor3 |= value & CyREC_FIFO;
-   base_addr[CyCOR3<<index] = info->cor3;
-   write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
-   return 0;
-}
 
 static int
 get_threshold(struct cyclades_port * info, unsigned long *value)
 {
-   unsigned char *base_addr;
-   int card,channel,chip,index;
-   unsigned long tmp;
+  unsigned char *base_addr;
+  int card,channel,chip,index;
+  unsigned long tmp;
    
-   card = info->card;
-   channel = info->line - cy_card[card].first_line;
-   chip = channel>>2;
-   channel &= 0x03;
-   index = cy_card[card].bus_index;
-   base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+	tmp = cy_readb(base_addr+(CyCOR3<<index)) & CyREC_FIFO;
+	cy_put_user(tmp,value);
+    } else {
+	// Nothing to do!
+    }
+    return 0;
+}/* get_threshold */
 
-   tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
-   put_fs_long(tmp,value);
-   return 0;
-}
 
 static int
 set_default_threshold(struct cyclades_port * info, unsigned long value)
 {
-   info->default_threshold = value & 0x0f;
-   return 0;
-}
+    info->default_threshold = value & 0x0f;
+    return 0;
+}/* set_default_threshold */
+
 
 static int
 get_default_threshold(struct cyclades_port * info, unsigned long *value)
 {
-   put_fs_long(info->default_threshold,value);
-   return 0;
-}
+    cy_put_user(info->default_threshold,value);
+    return 0;
+}/* get_default_threshold */
+
 
 static int
 set_timeout(struct cyclades_port * info, unsigned long value)
 {
-   unsigned char *base_addr;
-   int card,channel,chip,index;
+  unsigned char *base_addr;
+  int card,channel,chip,index;
    
-   card = info->card;
-   channel = info->line - cy_card[card].first_line;
-   chip = channel>>2;
-   channel &= 0x03;
-   index = cy_card[card].bus_index;
-   base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+	cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff);
+    } else {
+	// Nothing to do!
+    }
+    return 0;
+}/* set_timeout */
 
-   base_addr[CyRTPR<<index] = value & 0xff;
-   return 0;
-}
 
 static int
 get_timeout(struct cyclades_port * info, unsigned long *value)
 {
-   unsigned char *base_addr;
-   int card,channel,chip,index;
-   unsigned long tmp;
+  unsigned char *base_addr;
+  int card,channel,chip,index;
+  unsigned long tmp;
    
-   card = info->card;
-   channel = info->line - cy_card[card].first_line;
-   chip = channel>>2;
-   channel &= 0x03;
-   index = cy_card[card].bus_index;
-   base_addr = (unsigned char*)
-		   (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+	tmp = cy_readb(base_addr+(CyRTPR<<index));
+	cy_put_user(tmp,value);
+    } else {
+	// Nothing to do!
+    }
+    return 0;
+}/* get_timeout */
 
-   tmp = base_addr[CyRTPR<<index];
-   put_fs_long(tmp,value);
-   return 0;
-}
 
 static int
 set_default_timeout(struct cyclades_port * info, unsigned long value)
 {
-   info->default_timeout = value & 0xff;
-   return 0;
-}
+    info->default_timeout = value & 0xff;
+    return 0;
+}/* set_default_timeout */
+
 
 static int
 get_default_timeout(struct cyclades_port * info, unsigned long *value)
 {
-   put_fs_long(info->default_timeout,value);
-   return 0;
-}
+    cy_put_user(info->default_timeout,value);
+    return 0;
+}/* get_default_timeout */
 
+/*
+ * This routine allows the tty driver to implement device-
+ * specific ioctl's.  If the ioctl number passed in cmd is
+ * not recognized by the driver, it should return ENOIOCTLCMD.
+ */
 static int
 cy_ioctl(struct tty_struct *tty, struct file * file,
             unsigned int cmd, unsigned long arg)
@@ -2163,8 +4013,9 @@
   struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
   int ret_val = 0;
 
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */
+#ifdef CY_DEBUG_OTHER
+    printk("cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
+        info->line, cmd, arg); /* */
 #endif
 
     switch (cmd) {
@@ -2176,7 +4027,7 @@
                 break;
             }
             ret_val = get_mon_info(info, (struct cyclades_monitor *)arg);
-	    break;
+            break;
         case CYGETTHRESH:
             error = verify_area(VERIFY_WRITE, (void *) arg
                                 ,sizeof(unsigned long));
@@ -2184,11 +4035,11 @@
                 ret_val = error;
                 break;
             }
-	    ret_val = get_threshold(info, (unsigned long *)arg);
- 	    break;
+            ret_val = get_threshold(info, (unsigned long *)arg);
+            break;
         case CYSETTHRESH:
             ret_val = set_threshold(info, (unsigned long)arg);
-	    break;
+            break;
         case CYGETDEFTHRESH:
             error = verify_area(VERIFY_WRITE, (void *) arg
                                 ,sizeof(unsigned long));
@@ -2196,11 +4047,11 @@
                 ret_val = error;
                 break;
             }
-	    ret_val = get_default_threshold(info, (unsigned long *)arg);
- 	    break;
+            ret_val = get_default_threshold(info, (unsigned long *)arg);
+            break;
         case CYSETDEFTHRESH:
             ret_val = set_default_threshold(info, (unsigned long)arg);
-	    break;
+            break;
         case CYGETTIMEOUT:
             error = verify_area(VERIFY_WRITE, (void *) arg
                                 ,sizeof(unsigned long));
@@ -2208,11 +4059,11 @@
                 ret_val = error;
                 break;
             }
-	    ret_val = get_timeout(info, (unsigned long *)arg);
- 	    break;
+            ret_val = get_timeout(info, (unsigned long *)arg);
+            break;
         case CYSETTIMEOUT:
             ret_val = set_timeout(info, (unsigned long)arg);
-	    break;
+            break;
         case CYGETDEFTIMEOUT:
             error = verify_area(VERIFY_WRITE, (void *) arg
                                 ,sizeof(unsigned long));
@@ -2220,23 +4071,65 @@
                 ret_val = error;
                 break;
             }
-	    ret_val = get_default_timeout(info, (unsigned long *)arg);
- 	    break;
+            ret_val = get_default_timeout(info, (unsigned long *)arg);
+            break;
         case CYSETDEFTIMEOUT:
             ret_val = set_default_timeout(info, (unsigned long)arg);
+            break;
+	case CYSETRFLOW:
+    	    info->rflow = (int)arg;
+	    ret_val = 0;
+	    break;
+	case CYGETRFLOW:
+	    ret_val = info->rflow;
+	    break;
+	case CYSETRTSDTR_INV:
+    	    info->rtsdtr_inv = (int)arg;
+	    ret_val = 0;
+	    break;
+	case CYGETRTSDTR_INV:
+	    ret_val = info->rtsdtr_inv;
+	    break;
+	case CYGETCARDINFO:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(struct cyclades_card));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            copy_to_user((void *)arg, (void *)&cy_card[info->card], 
+			 sizeof (struct cyclades_card));
+	    ret_val = 0;
+            break;
+	case CYGETCD1400VER:
+	    ret_val = info->chip_rev;
+	    break;
+	case CYZSETPOLLCYCLE:
+            cyz_polling_cycle = (arg * HZ) / 1000;
+	    ret_val = 0;
+	    break;
+	case CYZGETPOLLCYCLE:
+            ret_val = (cyz_polling_cycle * 1000) / HZ;
+	    break;
+	case CYSETWAIT:
+    	    info->closing_wait = (unsigned short)arg * HZ/100;
+	    ret_val = 0;
+	    break;
+	case CYGETWAIT:
+	    ret_val = info->closing_wait / (HZ/100);
 	    break;
         case TCSBRK:    /* SVID version: non-zero arg --> no break */
-	    ret_val = tty_check_change(tty);
-	    if (ret_val)
-		return ret_val;
+            ret_val = tty_check_change(tty);
+            if (ret_val)
+                return ret_val;
             tty_wait_until_sent(tty,0);
             if (!arg)
                 send_break(info, HZ/4); /* 1/4 second */
             break;
         case TCSBRKP:   /* support for POSIX tcsendbreak() */
-	    ret_val = tty_check_change(tty);
-	    if (ret_val)
-		return ret_val;
+            ret_val = tty_check_change(tty);
+            if (ret_val)
+                return ret_val;
             tty_wait_until_sent(tty,0);
             send_break(info, arg ? arg*(HZ/10) : HZ/4);
             break;
@@ -2254,18 +4147,18 @@
                 ret_val = error;
                 break;
             }
-            put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+            cy_put_user(C_CLOCAL(tty) ? 1 : 0,
                         (unsigned long *) arg);
             break;
         case TIOCSSOFTCAR:
-            error = verify_area(VERIFY_READ, (void *) arg
-                                 ,sizeof(unsigned long *));
-            if (error) {
-                 ret_val = error;
-                 break;
-            }
+           error = verify_area(VERIFY_READ, (void *) arg
+                                ,sizeof(unsigned long *));
+           if (error) {
+                ret_val = error;
+                break;
+           }
 
-            arg = get_fs_long((unsigned long *) arg);
+            arg = cy_get_user((unsigned long *) arg);
             tty->termios->c_cflag =
                     ((tty->termios->c_cflag & ~CLOCAL) |
                      (arg ? CLOCAL : 0));
@@ -2300,31 +4193,35 @@
                                    (struct serial_struct *) arg);
             break;
         default:
-	    ret_val = -ENOIOCTLCMD;
+            ret_val = -ENOIOCTLCMD;
     }
 
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_ioctl done\n");
+#ifdef CY_DEBUG_OTHER
+    printk(" cyc:cy_ioctl done\n");
 #endif
 
     return ret_val;
 } /* cy_ioctl */
 
 
-
-
+/*
+ * This routine allows the tty driver to be notified when
+ * device's termios settings have changed.  Note that a
+ * well-designed tty driver should be prepared to accept the case
+ * where old == NULL, and try to do something rational.
+ */
 static void
 cy_set_termios(struct tty_struct *tty, struct termios * old_termios)
 {
   struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
 
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_set_termios ttyC%d\n", info->line);
+#ifdef CY_DEBUG_OTHER
+    printk("cyc:cy_set_termios ttyC%d\n", info->line);
 #endif
 
     if (tty->termios->c_cflag == old_termios->c_cflag)
         return;
-    config_setup(info);
+    set_line_char(info);
 
     if ((old_termios->c_cflag & CRTSCTS) &&
         !(tty->termios->c_cflag & CRTSCTS)) {
@@ -2341,353 +4238,281 @@
 } /* cy_set_termios */
 
 
+/*
+ * void (*set_ldisc)(struct tty_struct *tty);
+ *
+ * 	This routine allows the tty driver to be notified when the
+ * 	device's termios settings have changed.
+ * 
+ */
+
+
+/* This routine is called by the upper-layer tty layer to signal
+   that incoming characters should be throttled because the input
+   buffers are close to full.
+ */
 static void
-cy_close(struct tty_struct * tty, struct file * filp)
+cy_throttle(struct tty_struct * tty)
 {
-  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
   unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel,index;
 
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_close ttyC%d\n", info->line);
-#endif
+#ifdef CY_DEBUG_THROTTLE
+  char buf[64];
 
-    if (!info
-    || serial_paranoia_check(info, tty->device, "cy_close")){
-        return;
-    }
-#ifdef SERIAL_DEBUG_OPEN
-    printk("cy_close ttyC%d, count = %d\n", info->line, info->count);
+    printk("cyc:throttle %s: %d....ttyC%d\n", 
+	   _tty_name(tty, buf),
+           tty->ldisc.chars_in_buffer(tty), info->line);
 #endif
 
-    save_flags(flags); cli();
-
-    /* If the TTY is being hung up, nothing to do */
-    if (tty_hung_up_p(filp)) {
-	MOD_DEC_USE_COUNT;
-	restore_flags(flags);
-	return;
-    }
-	
-    if ((tty->count == 1) && (info->count != 1)) {
-	/*
-	 * Uh, oh.  tty->count is 1, which means that the tty
-	 * structure will be freed.  Info->count should always
-	 * be one in these conditions.  If it's greater than
-	 * one, we've got real problems, since it means the
-	 * serial port won't be shutdown.
-	 */
-	printk("cy_close: bad serial port count; tty->count is 1, "
-	   "info->count is %d\n", info->count);
-	info->count = 1;
-    }
-#ifdef SERIAL_DEBUG_COUNT
-    printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count - 1);
-#endif
-    if (--info->count < 0) {
-#ifdef SERIAL_DEBUG_COUNT
-    printk("cyc: %d: setting count to 0\n", __LINE__);
-#endif
-	info->count = 0;
+    if (serial_paranoia_check(info, tty->device, "cy_throttle")){
+            return;
     }
-    if (info->count)
-    {
-        MOD_DEC_USE_COUNT;
-	restore_flags(flags);
-	return;
+
+    if (I_IXOFF(tty)) {
+        info->x_char = STOP_CHAR(tty);
+            /* Should use the "Send Special Character" feature!!! */
     }
-    info->flags |= ASYNC_CLOSING;
-    /*
-     * Save the termios structure, since this port may have
-     * separate termios for callout and dialin.
-     */
-    if (info->flags & ASYNC_NORMAL_ACTIVE)
-	info->normal_termios = *tty->termios;
-    if (info->flags & ASYNC_CALLOUT_ACTIVE)
-	info->callout_termios = *tty->termios;
-    if (info->flags & ASYNC_INITIALIZED)
-	tty_wait_until_sent(tty, 30*HZ); /* 30 seconds timeout */
-    shutdown(info);
-    if (tty->driver.flush_buffer)
-	tty->driver.flush_buffer(tty);
-    if (tty->ldisc.flush_buffer)
-	tty->ldisc.flush_buffer(tty);
-    info->event = 0;
-    info->tty = 0;
-    if (info->blocked_open) {
-	if (info->close_delay) {
-	    current->state = TASK_INTERRUPTIBLE;
-	    current->timeout = jiffies + info->close_delay;
-	    schedule();
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+	save_flags(flags); cli();
+	cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+	if (info->rtsdtr_inv) {
+		cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+	} else {
+		cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
 	}
-	wake_up_interruptible(&info->open_wait);
+	restore_flags(flags);
+    } else {
+	// Nothing to do!
     }
-    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
-		     ASYNC_CLOSING);
-    wake_up_interruptible(&info->close_wait);
 
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_close done\n");
-#endif
-
-    MOD_DEC_USE_COUNT;
-    restore_flags(flags);
     return;
-} /* cy_close */
+} /* cy_throttle */
+
 
 /*
- * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ * This routine notifies the tty driver that it should signal
+ * that characters can now be sent to the tty without fear of
+ * overrunning the input buffers of the line disciplines.
  */
-void
-cy_hangup(struct tty_struct *tty)
+static void
+cy_unthrottle(struct tty_struct * tty)
 {
-  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
-	
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_hangup ttyC%d\n", info->line); /* */
-#endif
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel,index;
 
-    if (serial_paranoia_check(info, tty->device, "cy_hangup"))
-	return;
-    
-    shutdown(info);
-    info->event = 0;
-    info->count = 0;
-#ifdef SERIAL_DEBUG_COUNT
-    printk("cyc: %d(%d): setting count to 0\n", __LINE__, current->pid);
+#ifdef CY_DEBUG_THROTTLE
+  char buf[64];
+        
+    printk("cyc:unthrottle %s: %d....ttyC%d\n", 
+	   _tty_name(tty, buf),
+           tty->ldisc.chars_in_buffer(tty), info->line);
 #endif
-    info->tty = 0;
-    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
-    wake_up_interruptible(&info->open_wait);
-} /* cy_hangup */
 
+    if (serial_paranoia_check(info, tty->device, "cy_unthrottle")){
+            return;
+    }
 
+    if (I_IXOFF(tty)) {
+	if (info->x_char)
+	    info->x_char = 0;
+	else
+	    info->x_char = START_CHAR(tty);
+            /* Should use the "Send Special Character" feature!!! */
+    }
 
-/*
- * ------------------------------------------------------------
- * cy_open() and friends
- * ------------------------------------------------------------
- */
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    if (!IS_CYC_Z(cy_card[card])) {
+	chip = channel>>2;
+	channel &= 0x03;
+	index = cy_card[card].bus_index;
+	base_addr = (unsigned char*)
+		       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
 
-static int
-block_til_ready(struct tty_struct *tty, struct file * filp,
-                           struct cyclades_port *info)
+	save_flags(flags); cli();
+	cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+	if (info->rtsdtr_inv) {
+		cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+	} else {
+		cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+	}
+	restore_flags(flags);
+    }else{
+	// Nothing to do!
+    }
+
+    return;
+} /* cy_unthrottle */
+
+
+/* cy_start and cy_stop provide software output flow control as a
+   function of XON/XOFF, software CTS, and other such stuff.
+*/
+static void
+cy_stop(struct tty_struct *tty)
 {
-  struct wait_queue wait = { current, NULL };
   struct cyclades_card *cinfo;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned char *base_addr;
+  int chip,channel,index;
   unsigned long flags;
-  int chip, channel,index;
-  int retval;
-  char *base_addr;
 
-    /*
-     * If the device is in the middle of being closed, then block
-     * until it's done, and then try again.
-     */
-    if (info->flags & ASYNC_CLOSING) {
-	interruptible_sleep_on(&info->close_wait);
-	if (info->flags & ASYNC_HUP_NOTIFY){
-	    return -EAGAIN;
-	}else{
-	    return -ERESTARTSYS;
-	}
-    }
+#ifdef CY_DEBUG_OTHER
+    printk("cyc:cy_stop ttyC%d\n", info->line); /* */
+#endif
 
-    /*
-     * If this is a callout device, then just make sure the normal
-     * device isn't being used.
-     */
-    if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
-	if (info->flags & ASYNC_NORMAL_ACTIVE){
-	    return -EBUSY;
-	}
-	if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
-	    (info->flags & ASYNC_SESSION_LOCKOUT) &&
-	    (info->session != current->session)){
-	    return -EBUSY;
-	}
-	if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
-	    (info->flags & ASYNC_PGRP_LOCKOUT) &&
-	    (info->pgrp != current->pgrp)){
-	    return -EBUSY;
-	}
-	info->flags |= ASYNC_CALLOUT_ACTIVE;
-	return 0;
+    if (serial_paranoia_check(info, tty->device, "cy_stop"))
+        return;
+        
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    if (!IS_CYC_Z(*cinfo)) {
+        index = cinfo->bus_index;
+        chip = channel>>2;
+        channel &= 0x03;
+        base_addr = (unsigned char*)
+                   (cy_card[info->card].base_addr
+                           + (cy_chip_offset[chip]<<index));
+
+        save_flags(flags); cli();
+            cy_writeb((u_long)base_addr+(CyCAR<<index),
+	       (u_char)(channel & 0x0003)); /* index channel */
+            cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
+        restore_flags(flags);
+    } else {
+	// Nothing to do!
     }
 
-    /*
-     * If non-blocking mode is set, then make the check up front
-     * and then exit.
-     */
-    if (filp->f_flags & O_NONBLOCK) {
-	if (info->flags & ASYNC_CALLOUT_ACTIVE){
-	    return -EBUSY;
-	}
-	info->flags |= ASYNC_NORMAL_ACTIVE;
-	return 0;
-    }
+    return;
+} /* cy_stop */
 
-    /*
-     * 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
-     * cy_close() knows when to free things.  We restore it upon
-     * exit, either normal or abnormal.
-     */
-    retval = 0;
-    add_wait_queue(&info->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
-    printk("block_til_ready before block: ttyC%d, count = %d\n",
-	   info->line, info->count);/**/
-#endif
-    info->count--;
-#ifdef SERIAL_DEBUG_COUNT
-    printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count);
+
+static void
+cy_start(struct tty_struct *tty)
+{
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned char *base_addr;
+  int chip,channel,index;
+  unsigned long flags;
+
+#ifdef CY_DEBUG_OTHER
+    printk("cyc:cy_start ttyC%d\n", info->line); /* */
 #endif
-    info->blocked_open++;
 
+    if (serial_paranoia_check(info, tty->device, "cy_start"))
+        return;
+        
     cinfo = &cy_card[info->card];
     channel = info->line - cinfo->first_line;
-    chip = channel>>2;
-    channel &= 0x03;
     index = cinfo->bus_index;
-    base_addr = (char *) (cinfo->base_addr + (cy_chip_offset[chip]<<index));
-
-    while (1) {
-	save_flags(flags); cli();
-	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
-		base_addr[CyCAR<<index] = (u_char)channel;
-		base_addr[CyMSVR1<<index] = CyRTS;
-		base_addr[CyMSVR2<<index] = CyDTR;
-#ifdef SERIAL_DEBUG_DTR
-                printk("cyc: %d: raising DTR\n", __LINE__);
-                printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
-#endif
-	    }
-	restore_flags(flags);
-	current->state = TASK_INTERRUPTIBLE;
-	if (tty_hung_up_p(filp)
-	|| !(info->flags & ASYNC_INITIALIZED) ){
-	    if (info->flags & ASYNC_HUP_NOTIFY) {
-		retval = -EAGAIN;
-	    }else{
-		retval = -ERESTARTSYS;
-	    }
-	    break;
-	}
-	save_flags(flags); cli();
-	    base_addr[CyCAR<<index] = (u_char)channel;
-	    if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
-	    && !(info->flags & ASYNC_CLOSING)
-	    && (C_CLOCAL(tty)
-	        || (base_addr[CyMSVR1<<index] & CyDCD))) {
-		    restore_flags(flags);
-		    break;
-	    }
-	restore_flags(flags);
-	if (current->signal & ~current->blocked) {
-	    retval = -ERESTARTSYS;
-	    break;
-	}
-#ifdef SERIAL_DEBUG_OPEN
-	printk("block_til_ready blocking: ttyC%d, count = %d\n",
-	       info->line, info->count);/**/
-#endif
-	schedule();
-    }
-    current->state = TASK_RUNNING;
-    remove_wait_queue(&info->open_wait, &wait);
-    if (!tty_hung_up_p(filp)){
-	info->count++;
-#ifdef SERIAL_DEBUG_COUNT
-    printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count);
-#endif
+    if (!IS_CYC_Z(*cinfo)) {
+        chip = channel>>2;
+        channel &= 0x03;
+        base_addr = (unsigned char*)
+                       (cy_card[info->card].base_addr
+		       + (cy_chip_offset[chip]<<index));
+
+        save_flags(flags); cli();
+            cy_writeb((u_long)base_addr+(CyCAR<<index),
+	       (u_char)(channel & 0x0003)); /* index channel */
+            cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
+        restore_flags(flags);
+    } else {
+	// Nothing to do!
     }
-    info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
-    printk("block_til_ready after blocking: ttyC%d, count = %d\n",
-	   info->line, info->count);/**/
-#endif
-    if (retval)
-	    return retval;
-    info->flags |= ASYNC_NORMAL_ACTIVE;
-    return 0;
-} /* block_til_ready */
 
-/*
- * This routine is called whenever a serial port is opened.  It
- * performs the serial-specific initialization for the tty structure.
- */
-int
-cy_open(struct tty_struct *tty, struct file * filp)
-{
-  struct cyclades_port  *info;
-  int retval, line;
+    return;
+} /* cy_start */
 
-    line = MINOR(tty->device) - tty->driver.minor_start;
-    if ((line < 0) || (NR_PORTS <= line)){
-        return -ENODEV;
-    }
-    info = &cy_port[line];
-    if (info->line < 0){
-        return -ENODEV;
-    }
-#ifdef SERIAL_DEBUG_OTHER
-    printk("cy_open ttyC%d\n", info->line); /* */
-#endif
-    if (serial_paranoia_check(info, tty->device, "cy_open")){
-        return -ENODEV;
-    }
-#ifdef SERIAL_DEBUG_OPEN
-    printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/
-#endif
-    info->count++;
-#ifdef SERIAL_DEBUG_COUNT
-    printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count);
+
+static void
+cy_flush_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  int card, channel;
+  unsigned long flags;
+                                
+#ifdef CY_DEBUG_IO
+    printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */
 #endif
-    tty->driver_data = info;
-    info->tty = tty;
 
-    if (!tmp_buf) {
-	tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
-	if (!tmp_buf){
-	    return -ENOMEM;
-        }
-    }
+    if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
+        return;
+    save_flags(flags); cli();
+    info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+    restore_flags(flags);
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
 
-    if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
-	if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
-	    *tty->termios = info->normal_termios;
-	else 
-	    *tty->termios = info->callout_termios;
-    }
-    /*
-     * Start up serial port
-     */
-    retval = startup(info);
-    if (retval){
-	return retval;
+    if (IS_CYC_Z(cy_card[card])) { /* If it is a Z card, flush the on-board 
+				      buffers as well */
+	static volatile struct FIRM_ID *firm_id;
+	static volatile struct ZFW_CTRL *zfw_ctrl;
+	static volatile struct CH_CTRL *ch_ctrl;
+	static volatile struct BUF_CTRL *buf_ctrl;
+
+	firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS);
+	zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr +
+				cy_readl(&firm_id->zfwctrl_addr));
+	ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+	buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
+
+	while (cy_readl(&buf_ctrl->tx_get) != cy_readl(&buf_ctrl->tx_put))
+		cy_writel(&buf_ctrl->tx_put, cy_readl(&buf_ctrl->tx_get));
     }
+    wake_up_interruptible(&tty->write_wait);
+    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+	&& tty->ldisc.write_wakeup)
+	    (tty->ldisc.write_wakeup)(tty);
+} /* cy_flush_buffer */
 
-    MOD_INC_USE_COUNT;
 
-    retval = block_til_ready(tty, filp, info);
-    if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
-	printk("cy_open returning after block_til_ready with %d\n",
-	       retval);
+/*
+ * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void
+cy_hangup(struct tty_struct *tty)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+        
+#ifdef CY_DEBUG_OTHER
+    printk("cyc:cy_hangup ttyC%d\n", info->line); /* */
 #endif
-	return retval;
-    }
 
-    info->session = current->session;
-    info->pgrp = current->pgrp;
+    if (serial_paranoia_check(info, tty->device, "cy_hangup"))
+        return;
 
-#ifdef SERIAL_DEBUG_OPEN
-    printk("cy_open done\n");/**/
+    cy_flush_buffer(tty);
+    shutdown(info);
+    info->event = 0;
+    info->count = 0;
+#ifdef CY_DEBUG_COUNT
+    printk("cyc:cy_hangup (%d): setting count to 0\n", current->pid);
 #endif
-    return 0;
-} /* cy_open */
-
+    info->tty = 0;
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+    wake_up_interruptible(&info->open_wait);
+} /* cy_hangup */
 
 
 /*
@@ -2698,33 +4523,25 @@
  * ---------------------------------------------------------------------
  */
 
-/*
- * This routine prints out the appropriate serial driver version
- * number, and identifies which options were configured into this
- * driver.
- */
-static void
-show_version(void)
-{
-    printk("Cyclom driver %s\n",rcsid);
-} /* show_version */
-
-/* initialize chips on card -- return number of valid
+/* initialize chips on Cyclom-Y card -- return number of valid
    chips (which is number of ports/4) */
-int
-cy_init_card(unsigned char *true_base_addr,int index)
+__initfunc(static unsigned short
+cyy_init_card(volatile ucchar *true_base_addr,int index))
 {
   unsigned int chip_number;
-  unsigned char* base_addr;
+  volatile ucchar* base_addr;
 
-    true_base_addr[Cy_HwReset<<index] = 0; /* Cy_HwReset is 0x1400 */
-    true_base_addr[Cy_ClrIntr<<index] = 0; /* Cy_ClrIntr is 0x1800 */
+    cy_writeb((u_long)true_base_addr+(Cy_HwReset<<index), 0); 
+						/* Cy_HwReset is 0x1400 */
+    cy_writeb((u_long)true_base_addr+(Cy_ClrIntr<<index), 0); 
+						/* Cy_ClrIntr is 0x1800 */
     udelay(500L);
 
-    for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
-        base_addr = true_base_addr + (cy_chip_offset[chip_number]<<index);
+    for(chip_number=0; chip_number<CyMAX_CHIPS_PER_CARD; chip_number++){
+        base_addr = true_base_addr
+	               + (cy_chip_offset[chip_number]<<index);
         udelay(1000L);
-        if(base_addr[CyCCR<<index] != 0x00){
+        if(cy_readb(base_addr+(CyCCR<<index)) != 0x00){
             /*************
             printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
                chip_number, (unsigned long)base_addr);
@@ -2732,7 +4549,7 @@
             return chip_number;
         }
 
-        base_addr[CyGFRCR<<index] = 0;
+        cy_writeb((u_long)base_addr+(CyGFRCR<<index), 0);
         udelay(10L);
 
         /* The Cyclom-16Y does not decode address bit 9 and therefore
@@ -2742,39 +4559,629 @@
            and this must be a Cyclom-16Y, not a Cyclom-32Ye.
         */
         if (chip_number == 4
-        && *(true_base_addr + (cy_chip_offset[0]<<index) + (CyGFRCR<<index)) == 0){
-	    return chip_number;
+        && cy_readb(true_base_addr
+	    + (cy_chip_offset[0]<<index)
+	    + (CyGFRCR<<index)) == 0){
+            return chip_number;
         }
 
-        base_addr[CyCCR<<index] = CyCHIP_RESET;
+        cy_writeb((u_long)base_addr+(CyCCR<<index), CyCHIP_RESET);
         udelay(1000L);
 
-        if(base_addr[CyGFRCR<<index] == 0x00){
-	    /*
-            printk(" chip #%d at %#6lx is not responding (GFRCR stayed 0)\n",
+        if(cy_readb(base_addr+(CyGFRCR<<index)) == 0x00){
+            /*
+            printk(" chip #%d at %#6lx is not responding ",
                chip_number, (unsigned long)base_addr);
-	    */
+            printk("(GFRCR stayed 0)\n",
+            */
             return chip_number;
         }
-        if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){
-	    /*
+        if((0xf0 & (cy_readb(base_addr+(CyGFRCR<<index)))) != 0x40){
+            /*
             printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
-               chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]);
-	    */
+               chip_number, (unsigned long)base_addr,
+	       base_addr[CyGFRCR<<index]);
+            */
             return chip_number;
         }
-        base_addr[CyGCR<<index] = CyCH0_SERIAL;
-        base_addr[CyPPR<<index] = 244; /* better value than CyCLOCK_25_1MS * 5
-                                                  to run clock at 200 Hz */
+        cy_writeb((u_long)base_addr+(CyGCR<<index), CyCH0_SERIAL);
+        if (cy_readb(base_addr+(CyGFRCR<<index)) >= CD1400_REV_J){
+	    /* It is a CD1400 rev. J or later */
+	    /* Impossible to reach 5ms with this chip. 
+	       Changed to 2ms instead (f = 500 Hz). */
+	    cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_60_2MS);
+	} else {
+	    /* f = 200 Hz */
+	    cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_25_5MS);
+	}
 
-	/*
+    /*
         printk(" chip #%d at %#6lx is rev 0x%2x\n",
-               chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]);
-	*/
+               chip_number, (unsigned long)base_addr,
+	       cy_readb(base_addr+(CyGFRCR<<index)));
+    */
     }
-
     return chip_number;
-} /* cy_init_card */
+} /* cyy_init_card */
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
+ * sets global variables and return the number of ISA boards found.
+ * ---------------------------------------------------------------------
+ */
+__initfunc(static int
+cy_detect_isa(void))
+{
+  unsigned short	cy_isa_irq,nboard;
+  volatile ucchar	*cy_isa_address;
+  unsigned short	i,j,cy_isa_nchan;
+
+        nboard = 0;
+
+        /* scan the address table probing for Cyclom-Y/ISA boards */
+        for (i = 0 ; i < NR_ISA_ADDRS ; i++) {
+                cy_isa_address = cy_isa_addresses[i];
+                if (cy_isa_address  == 0x0000) {
+                        return(nboard);
+                }
+
+                /* probe for CD1400... */
+
+                cy_isa_nchan = CyPORTS_PER_CHIP * 
+                     cyy_init_card(cy_isa_address,0);
+                if (cy_isa_nchan == 0) {
+                        continue;
+                }
+
+                /* find out the board's irq by probing */
+                cy_isa_irq = do_auto_irq(cy_isa_address);
+                if (cy_isa_irq == 0) {
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
+                        printk("but the IRQ could not be detected.\n");
+                        continue;
+                }
+
+                if((cy_next_channel+cy_isa_nchan) > NR_PORTS) {
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
+                        printk("but no more channels are available.\n");
+                        printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+                        return(nboard);
+                }
+                /* fill the next cy_card structure available */
+                for (j = 0 ; j < NR_CARDS ; j++) {
+                        if (cy_card[j].base_addr == 0)  break;
+                }
+                if (j == NR_CARDS) {    /* no more cy_cards available */
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
+                        printk("but no more cards can be used .\n");
+                        printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+                        return(nboard);
+                }
+
+                /* allocate IRQ */
+                if(request_irq(cy_isa_irq, cyy_interrupt,
+				   SA_INTERRUPT, "cyclomY", NULL))
+                {
+                        printk("Cyclom-Y/ISA found at 0x%lx ",
+                                (unsigned long) cy_isa_address);
+                        printk("but could not allocate IRQ#%d.\n",
+                                cy_isa_irq);
+                        return(nboard);
+                }
+
+                /* set cy_card */
+                cy_card[j].base_addr = (u_long) cy_isa_address;
+                cy_card[j].ctl_addr = 0;
+                cy_card[j].irq = (int) cy_isa_irq;
+                cy_card[j].bus_index = 0;
+                cy_card[j].first_line = cy_next_channel;
+                cy_card[j].num_chips = cy_isa_nchan/4;
+                IRQ_cards[cy_isa_irq] = &cy_card[j];
+                nboard++;
+                        
+                /* print message */
+                printk("Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d, ",
+                    j+1, (unsigned long) cy_isa_address,
+                    (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
+		    cy_isa_irq);
+                printk("%d channels starting from port %d.\n",
+                        cy_isa_nchan, cy_next_channel);
+                cy_next_channel += cy_isa_nchan;
+        }
+        return(nboard);
+
+} /* cy_detect_isa */
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI.
+ * sets global variables and return the number of PCI boards found.
+ * ---------------------------------------------------------------------
+ */
+__initfunc(static int
+cy_detect_pci(void))
+{
+#ifdef CONFIG_PCI
+  unsigned char         cyy_bus, cyy_dev_fn, cyy_rev_id;
+  unsigned long         pci_intr_ctrl;
+  unsigned char         cy_pci_irq;
+  uclong                cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
+  unsigned short        i,j,cy_pci_nchan, plx_ver;
+  unsigned short        device_id,dev_index = 0,board_index = 0;
+  uclong		mailbox;
+  uclong		Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
+
+        if(pcibios_present() == 0) {    /* PCI bus not present */
+                return(0);
+        }
+        for (i = 0; i < NR_CARDS; i++) {
+                /* look for a Cyclades card by vendor and device id */
+                while((device_id = cy_pci_dev_id[dev_index]) != 0) {
+                        if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES,
+                                        device_id,board_index,
+                                        &cyy_bus, &cyy_dev_fn) != 0)
+                        {
+                                dev_index++;    /* try next device id */
+                                board_index = 0;
+                        } else {
+                                board_index++;
+                                break;          /* found a board */
+                        }
+                }
+
+		if (device_id == 0)
+		    break;
+
+                /* read PCI configuration area */
+                pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
+                                 PCI_INTERRUPT_LINE, &cy_pci_irq);
+                pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+                                  PCI_BASE_ADDRESS_0, 
+				  (unsigned int *) &cy_pci_addr0);
+                pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+                                  PCI_BASE_ADDRESS_1, 
+				  (unsigned int *) &cy_pci_addr1);
+                pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+                                  PCI_BASE_ADDRESS_2, 
+				  (unsigned int *) &cy_pci_addr2);
+                pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
+                                  PCI_REVISION_ID, &cyy_rev_id);
+
+    if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
+	   || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
+#ifdef CY_PCI_DEBUG
+            printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
+		cyy_bus, cyy_dev_fn);
+            printk("rev_id=%d) IRQ%d\n",
+		cyy_rev_id, (int)cy_pci_irq);
+            printk("Cyclom-Y/PCI:found  winaddr=0x%lx ioaddr=0x%lx\n",
+		(ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
+#endif
+                cy_pci_addr1  &= PCI_BASE_ADDRESS_IO_MASK;
+                cy_pci_addr2  &= PCI_BASE_ADDRESS_MEM_MASK;
+
+#if defined(__alpha__)
+                if (device_id  == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
+		    printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
+		        cyy_bus, cyy_dev_fn);
+		    printk("rev_id=%d) IRQ%d\n",
+		        cyy_rev_id, (int)cy_pci_irq);
+                    printk("Cyclom-Y/PCI:found  winaddr=0x%lx ioaddr=0x%lx\n",
+		        (ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
+	            printk("Cyclom-Y/PCI not supported for low addresses in "
+                           "Alpha systems.\n");
+		    i--;
+	            continue;
+                }
+#else
+                if ((ulong)cy_pci_addr2 >= 0x100000)  /* above 1M? */
+                    cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
+#endif
+
+#ifdef CY_PCI_DEBUG
+            printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ioaddr=0x%lx\n",
+		(u_long)cy_pci_addr2, (u_long)cy_pci_addr1);
+#endif
+                cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP * 
+                       cyy_init_card((volatile ucchar *)cy_pci_addr2, 1));
+                if(cy_pci_nchan == 0) {
+                        printk("Cyclom-Y PCI host card with ");
+                        printk("no Serial-Modules at 0x%lx.\n",
+			    (ulong) cy_pci_addr2);
+                        i--;
+                        continue;
+                }
+                if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+                        printk("Cyclom-Y/PCI found at 0x%lx ",
+			    (ulong) cy_pci_addr2);
+                        printk("but no channels are available.\n");
+                        printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+                        return(i);
+                }
+                /* fill the next cy_card structure available */
+                for (j = 0 ; j < NR_CARDS ; j++) {
+                        if (cy_card[j].base_addr == 0)  break;
+                }
+                if (j == NR_CARDS) {    /* no more cy_cards available */
+                        printk("Cyclom-Y/PCI found at 0x%lx ",
+			    (ulong) cy_pci_addr2);
+                        printk("but no more cards can be used.\n");
+                        printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+                        return(i);
+                }
+
+                /* allocate IRQ */
+                if(request_irq(cy_pci_irq, cyy_interrupt,
+		        SA_INTERRUPT, "cyclomY", NULL))
+                {
+                        printk("Cyclom-Y/PCI found at 0x%lx ",
+			    (ulong) cy_pci_addr2);
+                        printk("but could not allocate IRQ%d.\n",
+			    cy_pci_irq);
+                        return(i);
+                }
+
+                /* set cy_card */
+                cy_card[j].base_addr = (ulong)cy_pci_addr2;
+                cy_card[j].ctl_addr = 0;
+                cy_card[j].irq = (int) cy_pci_irq;
+                cy_card[j].bus_index = 1;
+                cy_card[j].first_line = cy_next_channel;
+                cy_card[j].num_chips = cy_pci_nchan/4;
+                IRQ_cards[cy_pci_irq] = &cy_card[j];
+
+		/* enable interrupts in the PCI interface */
+		plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
+                switch (plx_ver) {
+		    case PLX_9050:
+
+		    outw(inw(cy_pci_addr1+0x4c)|0x0040,cy_pci_addr1+0x4c);
+		    pci_intr_ctrl = (unsigned long)
+				(inw(cy_pci_addr1+0x4c)
+				| inw(cy_pci_addr1+0x4e)<<16);
+		    break;
+
+		    case PLX_9060:
+		    case PLX_9080:
+		    default: /* Old boards, use PLX_9060 */
+
+		    outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68);
+		    pci_intr_ctrl = (unsigned long)
+				(inw(cy_pci_addr1+0x68)
+				| inw(cy_pci_addr1+0x6a)<<16);
+		    break;
+                }
+
+                /* print message */
+                printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+		       j+1, 
+		       (ulong)cy_pci_addr2, 
+		       (ulong)(cy_pci_addr2 + CyPCI_Ywin - 1),
+		       (int)cy_pci_irq);
+                printk("%d channels starting from port %d.\n",
+		    cy_pci_nchan, cy_next_channel);
+
+                cy_next_channel += cy_pci_nchan;
+    }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
+	    /* print message */
+		printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
+		    cyy_bus, cyy_dev_fn);
+		printk("rev_id=%d) IRQ%d\n",
+		    cyy_rev_id, (int)cy_pci_irq);
+		printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+		    (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
+	    printk("Cyclades-Z/PCI not supported for low addresses\n");
+	    break;
+    }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
+#ifdef CY_PCI_DEBUG
+            printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
+		cyy_bus, cyy_dev_fn);
+            printk("rev_id=%d) IRQ%d\n",
+		cyy_rev_id, (int)cy_pci_irq);
+            printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+                (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
+#endif
+                cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK;
+#if !defined(__alpha__)
+                cy_pci_addr0 = (unsigned int) ioremap(
+                               cy_pci_addr0 & PAGE_MASK,
+                               PAGE_ALIGN(CyPCI_Zctl))
+                               + (cy_pci_addr0 & (PAGE_SIZE-1));
+#endif
+		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
+			   cy_pci_addr0)->mail_box_0);
+                cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK;
+		if (mailbox == ZE_V1) {
+#if !defined(__alpha__)
+               	    cy_pci_addr2 = (unsigned int) ioremap(
+	            	cy_pci_addr2 & PAGE_MASK,
+	            	PAGE_ALIGN(CyPCI_Ze_win))
+	            	+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
+		    if (ZeIndex == NR_CARDS) {
+			printk("Cyclades-Ze/PCI found at 0x%lx ",
+				(ulong)cy_pci_addr2);
+			printk("but no more cards can be used.\n");
+                        printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+		    } else {
+			Ze_addr0[ZeIndex] = cy_pci_addr0;
+			Ze_addr2[ZeIndex] = cy_pci_addr2;
+			ZeIndex++;
+		    }
+		    i--;
+		    continue;
+		} else {
+#if !defined(__alpha__)
+                    cy_pci_addr2 = (unsigned int) ioremap(
+			cy_pci_addr2 & PAGE_MASK,
+			PAGE_ALIGN(CyPCI_Zwin))
+			+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
+		}
+
+#ifdef CY_PCI_DEBUG
+            printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+                (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
+	    if (mailbox == ZO_V1) {
+		cy_writel(&((struct RUNTIME_9060 *)
+			  (cy_pci_addr0))->loc_addr_base, WIN_CREG);
+		PAUSE
+		printk("Cyclades-8Zo/PCI: FPGA id %lx, ver %lx\n",
+		       (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+		        (cy_pci_addr2))->fpga_id)),
+		       (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+		        (cy_pci_addr2))->fpga_version)));
+		cy_writel(&((struct RUNTIME_9060 *)
+			  (cy_pci_addr0))->loc_addr_base, WIN_RAM);
+	    } else {
+		printk("Cyclades-Z/PCI: New Cyclades-Z board.  FPGA not loaded\n");
+	    }
+#endif
+	    /* The following clears the firmware id word.  This ensures
+	       that the driver will not attempt to talk to the board
+	       until it has been properly initialized.
+	     */
+		PAUSE
+		if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
+		    cy_writel((ulong)(cy_pci_addr2+ID_ADDRESS), 0L);
+
+                /* This must be a Cyclades-8Zo/PCI.  The extendable
+                   version will have a different device_id and will
+                   be allocated its maximum number of ports. */
+                cy_pci_nchan = 8;
+
+                if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+                        printk("Cyclades-8Zo/PCI found at 0x%lx ",
+			    (ulong)cy_pci_addr2);
+                        printk("but no channels are available.\n");
+                        printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+                        return(i);
+                }
+
+                /* fill the next cy_card structure available */
+                for (j = 0 ; j < NR_CARDS ; j++) {
+                        if (cy_card[j].base_addr == 0)  break;
+                }
+                if (j == NR_CARDS) {    /* no more cy_cards available */
+		    printk("Cyclades-8Zo/PCI found at 0x%lx ",
+			(ulong)cy_pci_addr2);
+		    printk("but no more cards can be used.\n");
+                    printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+		    return(i);
+                }
+
+                /* allocate IRQ only if board has an IRQ */
+		if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
+		    if(request_irq(cy_pci_irq,cyz_interrupt,
+			SA_INTERRUPT,"cyclomZ",NULL))
+		    {
+			printk("Could not allocate IRQ%d ",
+			    cy_pci_irq);
+			printk("for Cyclades-8Zo/PCI at 0x%lx.\n",
+			    (ulong)cy_pci_addr2);
+			return(i);
+		    }
+		}
+
+
+                /* set cy_card */
+                cy_card[j].base_addr = cy_pci_addr2;
+                cy_card[j].ctl_addr = cy_pci_addr0;
+                cy_card[j].irq = (int) cy_pci_irq;
+                cy_card[j].bus_index = 1;
+                cy_card[j].first_line = cy_next_channel;
+                cy_card[j].num_chips = -1;
+                IRQ_cards[cy_pci_irq] = &cy_card[j];
+
+                /* print message */
+		/* don't report IRQ if board is no IRQ */
+		if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
+		    printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Zwin - 1),
+			(int)cy_pci_irq);
+		}else{
+		    printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Zwin - 1));
+		}
+                printk("%d channels starting from port %d.\n",
+		    cy_pci_nchan,cy_next_channel);
+                cy_next_channel += cy_pci_nchan;
+    }
+        }
+
+        for (; ZeIndex != 0 && i < NR_CARDS; i++) {
+	    cy_pci_addr0 = Ze_addr0[0];
+	    cy_pci_addr2 = Ze_addr2[0];
+	    for (j = 0 ; j < ZeIndex-1 ; j++) {
+		Ze_addr0[j] = Ze_addr0[j+1];
+		Ze_addr2[j] = Ze_addr2[j+1];
+	    }
+	    ZeIndex--;
+		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
+					   cy_pci_addr0)->mail_box_0);
+#ifdef CY_PCI_DEBUG
+            printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+                (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
+	    printk("Cyclades-Z/PCI: New Cyclades-Z board.  FPGA not loaded\n");
+#endif
+	    /* The following clears the firmware id word.  This ensures
+	       that the driver will not attempt to talk to the board
+	       until it has been properly initialized.
+	     */
+		PAUSE
+                /* This must be the new Cyclades-Ze/PCI. */
+                cy_pci_nchan = ZE_V1_NPORTS;
+
+                if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+                        printk("Cyclades-Ze/PCI found at 0x%lx ",
+			    (ulong)cy_pci_addr2);
+                        printk("but no channels are available.\n");
+                        printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+                        return(i);
+                }
+
+                /* fill the next cy_card structure available */
+                for (j = 0 ; j < NR_CARDS ; j++) {
+                        if (cy_card[j].base_addr == 0)  break;
+                }
+                if (j == NR_CARDS) {    /* no more cy_cards available */
+		    printk("Cyclades-Ze/PCI found at 0x%lx ",
+			(ulong)cy_pci_addr2);
+		    printk("but no more cards can be used.\n");
+                    printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+		    return(i);
+                }
+
+                /* allocate IRQ only if board has an IRQ */
+		if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
+		    if(request_irq(cy_pci_irq,cyz_interrupt,
+			SA_INTERRUPT,"cyclomZ",NULL))
+		    {
+			printk("Could not allocate IRQ%d ",
+			    cy_pci_irq);
+			printk("for Cyclades-Ze/PCI at 0x%lx.\n",
+			    (ulong) cy_pci_addr2);
+			return(i);
+		    }
+		}
+
+                /* set cy_card */
+                cy_card[j].base_addr = cy_pci_addr2;
+                cy_card[j].ctl_addr = cy_pci_addr0;
+                cy_card[j].irq = (int) cy_pci_irq;
+                cy_card[j].bus_index = 1;
+                cy_card[j].first_line = cy_next_channel;
+                cy_card[j].num_chips = -1;
+                IRQ_cards[cy_pci_irq] = &cy_card[j];
+
+                /* print message */
+		/* don't report IRQ if board is no IRQ */
+		if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
+		    printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1),
+			(int)cy_pci_irq);
+		}else{
+		    printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ",
+			j+1,(ulong)cy_pci_addr2,
+			(ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1));
+		}
+                printk("%d channels starting from port %d.\n",
+		    cy_pci_nchan,cy_next_channel);
+                cy_next_channel += cy_pci_nchan;
+        }
+	if (ZeIndex != 0) {
+	    printk("Cyclades-Ze/PCI found at 0x%x ",
+		(unsigned int) Ze_addr2[0]);
+	    printk("but no more cards can be used.\n");
+            printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+	}
+        return(i);
+#else
+        return(0);
+#endif /* ifdef CONFIG_PCI */
+} /* cy_detect_pci */
+
+
+/*
+ * This routine prints out the appropriate serial driver version number
+ * and identifies which options were configured into this driver.
+ */
+static inline void
+show_version(void)
+{
+  char *rcsvers, *rcsdate, *tmp;
+    rcsvers = strchr(rcsid, ' '); rcsvers++;
+    tmp = strchr(rcsvers, ' '); *tmp++ = '\0';
+    rcsdate = strchr(tmp, ' '); rcsdate++;
+    tmp = strrchr(rcsdate, ' '); *tmp = '\0';
+    printk("Cyclades driver %s %s\n",
+        rcsvers, rcsdate);
+    printk("        built %s %s\n",
+	__DATE__, __TIME__);
+} /* show_version */
+
+#if defined(CONFIG_PROC_FS) && !defined(MODULE)
+int cyclades_get_proc_info(char *buf, char **start, off_t offset, int length,
+                          int dummy)
+{
+    struct cyclades_port  *info;
+    int i;
+    int len=0;
+    off_t begin=0;
+    off_t pos=0;
+    int size;
+    __u32 cur_jifs = jiffies;
+
+    size = sprintf(buf+len, "Dev TimeOpen   BytesOut  IdleOut    BytesIn   IdleIn  Overruns  Ldisc\n");
+
+    len += size;
+    pos += size;
+
+    /* Output one line for each known port */
+    for (i = 0; i < NR_PORTS && cy_port[i].line >= 0; i++) {
+	info = &cy_port[i];
+
+	if (info->count)
+	    size = sprintf(buf+len,
+			"%3d %8lu %10lu %8lu %10lu %8lu %9lu %6d\n",
+			info->line,
+			JIFFIES_DIFF(info->idle_stats.in_use, cur_jifs) / HZ,
+			info->idle_stats.xmit_bytes,
+			JIFFIES_DIFF(info->idle_stats.xmit_idle, cur_jifs) / HZ,
+			info->idle_stats.recv_bytes,
+			JIFFIES_DIFF(info->idle_stats.recv_idle, cur_jifs) / HZ,
+			info->idle_stats.overruns,
+			info->tty->ldisc.num);
+	else
+	    size = sprintf(buf+len,
+			"%3d %8lu %10lu %8lu %10lu %8lu %9lu %6ld\n",
+			info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L);
+	len += size;
+	pos = begin + len;
+
+	if (pos < offset) {
+	    len   = 0;
+	    begin = pos;
+	}
+	if (pos > offset + length)
+	    break;
+    }
+
+    *start = buf + (offset - begin);	/* Start of wanted data */
+    len -= (offset - begin);		/* Start slop */
+    if (len > length)
+	len = length;			/* Ending slop */
+    return len;
+}
+#endif
+
 
 /* The serial driver boot-time initialization code!
     Hardware I/O ports are mapped to character special devices on a
@@ -2789,15 +5196,21 @@
     device driver because the Cyclom is more properly a multiplexer,
     not just an aggregation of serial ports on one card.
 
-    If there are more cards with more ports than have been statically
-    allocated above, a warning is printed and the extra ports are ignored.
+    If there are more cards with more ports than have been
+    statically allocated above, a warning is printed and the
+    extra ports are ignored.
  */
-int
-cy_init(void)
+
+__initfunc(int
+cy_init(void))
 {
-  struct cyclades_port	*info;
+  struct cyclades_port  *info;
   struct cyclades_card *cinfo;
-  int			board,port,i;
+  int number_z_boards = 0;
+  int board,port,i,index;
+  unsigned long mailbox;
+  unsigned short chip_number;
+  int nports;
 
     show_version();
 
@@ -2813,7 +5226,7 @@
     cy_serial_driver.subtype = SERIAL_TYPE_NORMAL;
     cy_serial_driver.init_termios = tty_std_termios;
     cy_serial_driver.init_termios.c_cflag =
-	    B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+            B9600 | CS8 | CREAD | HUPCL | CLOCAL;
     cy_serial_driver.flags = TTY_DRIVER_REAL_RAW;
     cy_serial_driver.refcount = &serial_refcount;
     cy_serial_driver.table = serial_table;
@@ -2845,31 +5258,31 @@
     cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 
     if (tty_register_driver(&cy_serial_driver))
-	    panic("Couldn't register Cyclom serial driver\n");
+            panic("Couldn't register Cyclades serial driver\n");
     if (tty_register_driver(&cy_callout_driver))
-	    panic("Couldn't register Cyclom callout driver\n");
+            panic("Couldn't register Cyclades callout driver\n");
 
     init_bh(CYCLADES_BH, do_cyclades_bh);
 
     for (i = 0; i < 16; i++) {
-	    IRQ_cards[i] = 0;
+            IRQ_cards[i] = 0;
     }
 
     for (i = 0; i < NR_CARDS; i++) {
-	    /* base_addr=0 indicates board not found */
-	    cy_card[i].base_addr = 0;
+            /* base_addr=0 indicates board not found */
+            cy_card[i].base_addr = 0;
     }
 
     /* the code below is responsible to find the boards. Each different
        type of board has its own detection routine. If a board is found,
        the next cy_card structure available is set by the detection
-       routine. These functions are responsible for checking the availability
-       of cy_card and cy_port data structures and updating the
-       cy_next_channel. */
+       routine. These functions are responsible for checking the
+       availability of cy_card and cy_port data structures and updating
+       the cy_next_channel. */
 
     /* look for isa boards */
     cy_isa_nboard = cy_detect_isa();
-
+    
     /* look for pci boards */
     cy_pci_nboard = cy_detect_pci();
 
@@ -2877,288 +5290,229 @@
 
     /* invalidate remaining cy_card structures */
     for (i = 0 ; i < NR_CARDS ; i++) {
-	if (cy_card[i].base_addr == 0) {
-		cy_card[i].first_line = -1;
-	}
+        if (cy_card[i].base_addr == 0) {
+                cy_card[i].first_line = -1;
+                cy_card[i].ctl_addr = 0;
+                cy_card[i].irq = 0;
+                cy_card[i].bus_index = 0;
+                cy_card[i].first_line = 0;
+                cy_card[i].num_chips = 0;
+        }
     }
     /* invalidate remaining cy_port structures */
     for (i = cy_next_channel ; i < NR_PORTS ; i++) {
-	cy_port[i].line = -1;
-	cy_port[i].magic = -1;
+        cy_port[i].line = -1;
+        cy_port[i].magic = -1;
     }
 
     /* initialize per-port data structures for each valid board found */
     for (board = 0 ; board < cy_nboard ; board++) {
-	    cinfo = &cy_card[board];
-	    for (port = cinfo->first_line ;
-		 port < cinfo->first_line + 4*cinfo->num_chips ;
-		 port++)
-	    {
-		info = &cy_port[port];
-		info->magic = CYCLADES_MAGIC;
-		info->type = PORT_CIRRUS;
-		info->card = board;
-		info->line = port;
-		info->flags = STD_COM_FLAGS;
-		info->tty = 0;
-		info->xmit_fifo_size = 12;
-		info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS;
-		info->cor2 = CyETC;
-		info->cor3 = 0x08; /* _very_ small receive threshold */
-		info->cor4 = 0;
-		info->cor5 = 0;
-		info->tbpr = baud_bpr[13]; /* Tx BPR */
-		info->tco = baud_co[13]; /* Tx CO */
-		info->rbpr = baud_bpr[13]; /* Rx BPR */
-		info->rco = baud_co[13]; /* Rx CO */
-		info->close_delay = 0;
-		info->x_char = 0;
-		info->event = 0;
-		info->count = 0;
-#ifdef SERIAL_DEBUG_COUNT
-    printk("cyc: %d: setting count to 0\n", __LINE__);
-#endif
-		info->blocked_open = 0;
-		info->default_threshold = 0;
-		info->default_timeout = 0;
-		info->tqueue.routine = do_softint;
-		info->tqueue.data = info;
-		info->callout_termios =cy_callout_driver.init_termios;
-		info->normal_termios = cy_serial_driver.init_termios;
-		info->open_wait = 0;
-		info->close_wait = 0;
-		/* info->session */
-		/* info->pgrp */
-		info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK
-                                       | CyPARITY| CyFRAME| CyOVERRUN;
-		/* info->timeout */
-	    }
+            cinfo = &cy_card[board];
+            if (cinfo->num_chips == -1){ /* Cyclades-Z */
+		number_z_boards++;
+		mailbox = cy_readl(&((struct RUNTIME_9060 *)
+			     cy_card[board].ctl_addr)->mail_box_0);
+		nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
+                for (port = cinfo->first_line ;
+                     port < cinfo->first_line + nports;
+                     port++)
+                {
+                    info = &cy_port[port];
+                    info->magic = CYCLADES_MAGIC;
+                    info->type = PORT_STARTECH;
+                    info->card = board;
+                    info->line = port;
+		    info->chip_rev = 0;
+                    info->flags = STD_COM_FLAGS;
+                    info->tty = 0;
+		    if (mailbox == ZO_V1)
+			info->xmit_fifo_size = CYZ_FIFO_SIZE;
+		    else
+			info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE;
+                    info->cor1 = 0;
+                    info->cor2 = 0;
+                    info->cor3 = 0;
+                    info->cor4 = 0;
+                    info->cor5 = 0;
+                    info->tbpr = 0;
+                    info->tco = 0;
+                    info->rbpr = 0;
+                    info->rco = 0;
+                    info->close_delay = 5*HZ/10;
+		    info->closing_wait = CLOSING_WAIT_DELAY;
+                    info->x_char = 0;
+                    info->event = 0;
+                    info->count = 0;
+#ifdef CY_DEBUG_COUNT
+//        printk("cyc:cy_init(1) setting Z count to 0\n");
+#endif
+                    info->blocked_open = 0;
+                    info->default_threshold = 0;
+                    info->default_timeout = 0;
+                    info->tqueue.routine = do_softint;
+                    info->tqueue.data = info;
+                    info->callout_termios =
+		                cy_callout_driver.init_termios;
+                    info->normal_termios =
+		                cy_serial_driver.init_termios;
+                    info->open_wait = 0;
+                    info->close_wait = 0;
+                    info->shutdown_wait = 0;
+                    /* info->session */
+                    /* info->pgrp */
+                    info->read_status_mask = 0;
+                    /* info->timeout */
+		    /* Bentson's vars */
+                    info->jiffies[0] = 0;
+                    info->jiffies[1] = 0;
+                    info->jiffies[2] = 0;
+                    info->rflush_count = 0;
+                }
+                continue;
+            }else{ /* Cyclom-Y of some kind*/
+                index = cinfo->bus_index;
+                for (port = cinfo->first_line ;
+                     port < cinfo->first_line + 4*cinfo->num_chips ;
+                     port++)
+                {
+                    info = &cy_port[port];
+                    info->magic = CYCLADES_MAGIC;
+                    info->type = PORT_CIRRUS;
+                    info->card = board;
+                    info->line = port;
+                    info->flags = STD_COM_FLAGS;
+                    info->tty = 0;
+                    info->xmit_fifo_size = CyMAX_CHAR_FIFO;
+                    info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS;
+                    info->cor2 = CyETC;
+                    info->cor3 = 0x08; /* _very_ small rcv threshold */
+                    info->cor4 = 0;
+                    info->cor5 = 0;
+                    info->close_delay = 5*HZ/10;
+		    info->closing_wait = CLOSING_WAIT_DELAY;
+		    chip_number = (port - cinfo->first_line) / 4;
+                    if ((info->chip_rev = cy_readb(cinfo->base_addr +
+	               		 (cy_chip_offset[chip_number]<<index) +
+				 (CyGFRCR<<index))) >= CD1400_REV_J) {
+                        /* It is a CD1400 rev. J or later */
+                        info->tbpr = baud_bpr_60[13]; /* Tx BPR */
+                        info->tco = baud_co_60[13]; /* Tx CO */
+                        info->rbpr = baud_bpr_60[13]; /* Rx BPR */
+                        info->rco = baud_co_60[13]; /* Rx CO */
+                        info->rflow = 0;
+                        info->rtsdtr_inv = 1;
+                    } else {
+                        info->tbpr = baud_bpr_25[13]; /* Tx BPR */
+                        info->tco = baud_co_25[13]; /* Tx CO */
+                        info->rbpr = baud_bpr_25[13]; /* Rx BPR */
+                        info->rco = baud_co_25[13]; /* Rx CO */
+                        info->rflow = 0;
+                        info->rtsdtr_inv = 0;
+                    }
+                    info->x_char = 0;
+                    info->event = 0;
+                    info->count = 0;
+#ifdef CY_DEBUG_COUNT
+//        printk("cyc:cy_init(2) setting Y count to 0\n");
+#endif
+                    info->blocked_open = 0;
+                    info->default_threshold = 0;
+                    info->default_timeout = 0;
+                    info->tqueue.routine = do_softint;
+                    info->tqueue.data = info;
+                    info->callout_termios =
+		               cy_callout_driver.init_termios;
+                    info->normal_termios =
+		               cy_serial_driver.init_termios;
+                    info->open_wait = 0;
+                    info->close_wait = 0;
+                    info->shutdown_wait = 0;
+                    /* info->session */
+                    /* info->pgrp */
+                    info->read_status_mask =
+		                  CyTIMEOUT| CySPECHAR| CyBREAK
+                                  | CyPARITY| CyFRAME| CyOVERRUN;
+                    /* info->timeout */
+                }
+            }
+    }
+    
+    if ( number_z_boards && !cyz_timeron){
+        cyz_timeron++;
+	cyz_timerlist.expires = jiffies + 1;
+	add_timer(&cyz_timerlist);
+#ifdef CY_PCI_DEBUG
+	printk("Cyclades-Z polling initialized\n");
+#endif
     }
+
+#if defined(CONFIG_PROC_FS) && !defined(MODULE)
+    proc_register_dynamic(&proc_root, &cyclades_proc_entry);
+#endif
+
     return 0;
     
 } /* cy_init */
 
 #ifdef MODULE
+/* See linux/drivers/char/riscom.c for ideas on how to
+   pass additional base addresses to the driver!!! */
 int
 init_module(void)
 {
    return(cy_init());
-}
+} /* init_module */
 
 void
 cleanup_module(void)
 {
     int i;
+    unsigned long flags;
+
+    if (cyz_timeron){
+	cyz_timeron = 0;
+	del_timer(&cyz_timerlist);
+    }
 
+    save_flags(flags); cli();
 
+    free_page((unsigned long)tmp_buf);
     if (tty_unregister_driver(&cy_callout_driver))
-	    printk("Couldn't unregister Cyclom callout driver\n");
+            printk("Couldn't unregister Cyclades callout driver\n");
     if (tty_unregister_driver(&cy_serial_driver))
-	    printk("Couldn't unregister Cyclom serial driver\n");
+            printk("Couldn't unregister Cyclades serial driver\n");
+
+    restore_flags(flags);
 
     for (i = 0; i < NR_CARDS; i++) {
-        if (cy_card[i].base_addr != 0)
-	{
-	    free_irq(cy_card[i].irq,NULL);
-	}
+        if (cy_card[i].base_addr != 0
+	    && cy_card[i].irq)
+        {
+            free_irq(cy_card[i].irq,NULL);
+        }
     }
-}
-#endif
-
-/*
- * ---------------------------------------------------------------------
- * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
- * sets global variables and return the number of ISA boards found.
- * ---------------------------------------------------------------------
- */
-int
-cy_detect_isa()
-{
-  unsigned int		cy_isa_irq,nboard;
-  unsigned char		*cy_isa_address;
-  unsigned short	i,j,cy_isa_nchan;
-
-	nboard = 0;
-
-	/* scan the address table probing for Cyclom-Y/ISA boards */
-	for (i = 0 ; i < NR_ISA_ADDRESSES ; i++) {
-		cy_isa_address = cy_isa_addresses[i];
-		if (cy_isa_address  == 0x0000) {
-			return(nboard);
-		}
-
-		/* probe for CD1400... */
-		cy_isa_nchan = 4 * cy_init_card(cy_isa_address,0);
-		if (cy_isa_nchan == 0) {
-			continue;
-		}
-
-		/* find out the board's irq by probing */
-		cy_isa_irq = do_auto_irq(cy_isa_address);
-		if (cy_isa_irq == 0) {
-			printk("Cyclom-Y/ISA found at 0x%x but the IRQ could not be detected.\n",
-				(unsigned int) cy_isa_address);
-			continue;
-		}
-
-		if((cy_next_channel+cy_isa_nchan) > NR_PORTS) {
-			printk("Cyclom-Y/ISA found at 0x%x but no more channel structures are available.\n",
-				(unsigned int) cy_isa_address);
-			return(nboard);
-		}
-		/* fill the next cy_card structure available */
-		for (j = 0 ; j < NR_CARDS ; j++) {
-			if (cy_card[j].base_addr == 0)	break;
-		}
-		if (j == NR_CARDS) {	/* no more cy_cards available */
-			printk("Cyclom-Y/ISA found at 0x%x but no more card structures are available.\n",
-				(unsigned int) cy_isa_address);
-			return(nboard);
-		}
-
-		/* allocate IRQ */
-		if(request_irq(cy_isa_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL))
-		{
-			printk("Cyclom-Y/ISA found at 0x%x but could not allocate interrupt IRQ#%d.\n",
-				(unsigned int) cy_isa_address,cy_isa_irq);
-			return(nboard);
-		}
-
-		/* set cy_card */
-		cy_card[j].base_addr = (int) cy_isa_address;
-		cy_card[j].irq = (int) cy_isa_irq;
-		cy_card[j].bus_index = 0;
-		cy_card[j].first_line = cy_next_channel;
-		cy_card[j].num_chips = cy_isa_nchan/4;
-	        IRQ_cards[cy_isa_irq] = &cy_card[j];
-		nboard++;
-			
-		/* print message */
-		printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n",
-			j+1,(unsigned int) cy_isa_address,
-			(unsigned int)(cy_isa_address + 0x1fff),
-			cy_isa_irq,cy_isa_nchan,cy_next_channel);
-		cy_next_channel += cy_isa_nchan;
-	}
-	return(nboard);
-
-}
-
-/*
- * ---------------------------------------------------------------------
- * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI.
- * sets global variables and return the number of PCI boards found.
- * ---------------------------------------------------------------------
- */
-int
-cy_detect_pci()
+} /* cleanup_module */
+#else
+/* called by linux/init/main.c to parse command line options */
+void
+cy_setup(char *str, int *ints)
 {
-#ifdef CONFIG_PCI
-  unsigned char		cyy_bus, cyy_dev_fn, cyy_rev_id;
-  unsigned long		pci_intr_ctrl;
-  unsigned char		cy_pci_irq;
-  unsigned int		cy_pci_address, cy_pci_io;
-  unsigned short	i,j,cy_pci_nchan;
-  unsigned short	device_id,dev_index = 0,board_index = 0;
-
-	if(pcibios_present() == 0) {	/* PCI bus not present */
-	 	return(0);
-	}
-	for (i = 0; i < NR_CARDS; i++) {
-		/* look for a Cyclom-Y card by vendor and device id */
-		while((device_id = cy_pci_dev_id[dev_index]) != 0) {
-			if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES,
-					device_id,board_index,
-  					&cyy_bus, &cyy_dev_fn) != 0)
-			{
-				dev_index++;	/* try next device id */
-				board_index = 0;
-			} else {
-				board_index++;
-				break;		/* found a board */
-			}
-  		}
-		if (device_id == 0)	break;
-
-		/* read PCI configuration area */
-		pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
-				 PCI_INTERRUPT_LINE, &cy_pci_irq);
-		pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
-				  PCI_BASE_ADDRESS_1, &cy_pci_io);
-		pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
-				  PCI_BASE_ADDRESS_2, &cy_pci_address);
-		pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
-				  PCI_REVISION_ID, &cyy_rev_id);
-		cy_pci_address &= 0xfffffff0;
-		if ((ulong)cy_pci_address >= 0x100000) { /* above 1M? */
-			cy_pci_address =
-			    (unsigned int) vremap(cy_pci_address,0x4000);
-		}
-		cy_pci_io  &= 0xfffffffc;
-		cy_pci_nchan = 4 * cy_init_card((unsigned char *)
-						cy_pci_address,1);
-		if(cy_pci_nchan == 0) {
-			printk("Cyclom-Y PCI host card with no Serial-Modules at 0x%x.\n",
-				(unsigned int) cy_pci_address);
-			continue;
-		}
-		if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
-			printk("Cyclom-Y/PCI found at 0x%x but no more channel structures are available.\n",
-				(unsigned int) cy_pci_address);
-			return(i);
-		}
-#ifdef CY_PCI_DEBUG
-		printk("Cyclom-Ye/PCI #%d (bus=0x0%x, pci_id=0x%x, rev_id=%d).\n",
-			i+1,cyy_bus,cyy_dev_fn,cyy_rev_id);
-		printk("Cyclom-Ye/PCI: found at 0x%x, IRQ%d, ioaddr = 0x%lx.\n",
-			cy_pci_address,(int)cy_pci_irq,cy_pci_io);
-#endif
-		/* fill the next cy_card structure available */
-		for (j = 0 ; j < NR_CARDS ; j++) {
-			if (cy_card[j].base_addr == 0)	break;
-		}
-		if (j == NR_CARDS) {	/* no more cy_cards available */
-			printk("Cyclom-Y/PCI found at 0x%x but no more card structures are available.\n",
-				(unsigned int) cy_pci_address);
-			return(i);
-		}
-
-		/* allocate IRQ */
-		if(request_irq(cy_pci_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL))
-		{
-			printk("Cyclom-Y/PCI found at 0x%x but could not allocate interrupt IRQ%d.\n",
-				(unsigned int) cy_pci_address,cy_pci_irq);
-			return(i);
-		}
+  int i, j;
 
-		/* set cy_card */
-		cy_card[j].base_addr = (int) cy_pci_address;
-		cy_card[j].irq = (int) cy_pci_irq;
-		cy_card[j].bus_index = 1;
-		cy_card[j].first_line = cy_next_channel;
-		cy_card[j].num_chips = cy_pci_nchan/4;
-	        IRQ_cards[cy_pci_irq] = &cy_card[j];
-
-		/* enable interrupts in the PCI interface */
-		outw(inw(cy_pci_io+0x68)|0x0900,cy_pci_io+0x68);
-		pci_intr_ctrl = (unsigned long)(inw(cy_pci_io+0x68) | inw(cy_pci_io+0x6a)<<16);
-
-		/* print message */
-		printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n",
-			j+1,(unsigned int) cy_pci_address,
-			(unsigned int)(cy_pci_address + 0x3fff),
-			(int)cy_pci_irq,cy_pci_nchan,cy_next_channel);
+    for (i = 0 ; i < NR_ISA_ADDRS ; i++) {
+        if (cy_isa_addresses[i] == 0) break;
+    }
+    for (j = 1; j <= ints[0]; j++){
+        if ( i < NR_ISA_ADDRS ){
+            cy_isa_addresses[i++] = (unsigned char *)(ints[j]);
+        }
+    }
 
-		cy_next_channel += cy_pci_nchan;
-	}
-	return(i);
-#else
-	return(0);
-#endif /* ifdef CONFIG_PCI */
-}
+} /* cy_setup */
+#endif
 
 
-#ifdef CYCLOM_SHOW_STATUS
+#ifdef CY_SHOW_STATUS
 static void
 show_status(int line_num)
 {
@@ -3183,7 +5537,8 @@
     printk(" cy_port\n");
     printk("  card line flags = %d %d %x\n",
                  info->card, info->line, info->flags);
-    printk("  *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
+    printk("  *tty read_status_mask timeout xmit_fifo_size ",
+    printk("= %lx %x %x %x\n",
                  (long)info->tty, info->read_status_mask,
                  info->timeout, info->xmit_fifo_size);
     printk("  cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n",
@@ -3200,59 +5555,60 @@
 
     save_flags(flags); cli();
 
-	base_addr = (unsigned char*)
-		       (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+        base_addr = (unsigned char*)
+                       (cy_card[card].base_addr
+		       + (cy_chip_offset[chip]<<index));
 
 /* Global Registers */
 
-	printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]);
-	printk(" CyCAR %x\n", base_addr[CyCAR<<index]);
-	printk(" CyGCR %x\n", base_addr[CyGCR<<index]);
-	printk(" CySVRR %x\n", base_addr[CySVRR<<index]);
-	printk(" CyRICR %x\n", base_addr[CyRICR<<index]);
-	printk(" CyTICR %x\n", base_addr[CyTICR<<index]);
-	printk(" CyMICR %x\n", base_addr[CyMICR<<index]);
-	printk(" CyRIR %x\n", base_addr[CyRIR<<index]);
-	printk(" CyTIR %x\n", base_addr[CyTIR<<index]);
-	printk(" CyMIR %x\n", base_addr[CyMIR<<index]);
-	printk(" CyPPR %x\n", base_addr[CyPPR<<index]);
+        printk(" CyGFRCR %x\n", cy_readb(base_addr + CyGFRCR<<index));
+        printk(" CyCAR %x\n", cy_readb(base_addr + CyCAR<<index));
+        printk(" CyGCR %x\n", cy_readb(base_addr + CyGCR<<index));
+        printk(" CySVRR %x\n", cy_readb(base_addr + CySVRR<<index));
+        printk(" CyRICR %x\n", cy_readb(base_addr + CyRICR<<index));
+        printk(" CyTICR %x\n", cy_readb(base_addr + CyTICR<<index));
+        printk(" CyMICR %x\n", cy_readb(base_addr + CyMICR<<index));
+        printk(" CyRIR %x\n", cy_readb(base_addr + CyRIR<<index));
+        printk(" CyTIR %x\n", cy_readb(base_addr + CyTIR<<index));
+        printk(" CyMIR %x\n", cy_readb(base_addr + CyMIR<<index));
+        printk(" CyPPR %x\n", cy_readb(base_addr + CyPPR<<index));
 
-	base_addr[CyCAR<<index] = (u_char)channel;
+        cy_writeb(base_addr + CyCAR<<index, (u_char)channel);
 
 /* Virtual Registers */
 
-	printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]);
-	printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]);
-	printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]);
-	printk(" CyMISR %x\n", base_addr[CyMISR<<index]);
+        printk(" CyRIVR %x\n", cy_readb(base_addr + CyRIVR<<index));
+        printk(" CyTIVR %x\n", cy_readb(base_addr + CyTIVR<<index));
+        printk(" CyMIVR %x\n", cy_readb(base_addr + CyMIVR<<index));
+        printk(" CyMISR %x\n", cy_readb(base_addr + CyMISR<<index));
 
 /* Channel Registers */
 
-	printk(" CyCCR %x\n", base_addr[CyCCR<<index]);
-	printk(" CySRER %x\n", base_addr[CySRER<<index]);
-	printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]);
-	printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]);
-	printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]);
-	printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]);
-	printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]);
-	printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]);
-	printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]);
-	printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]);
-	printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]);
-	printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]);
-	printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]);
-	printk(" CySCRL %x\n", base_addr[CySCRL<<index]);
-	printk(" CySCRH %x\n", base_addr[CySCRH<<index]);
-	printk(" CyLNC %x\n", base_addr[CyLNC<<index]);
-	printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]);
-	printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]);
-	printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]);
-	printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]);
-	printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]);
-	printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]);
-	printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]);
-	printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]);
-	printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]);
+        printk(" CyCCR %x\n", cy_readb(base_addr + CyCCR<<index));
+        printk(" CySRER %x\n", cy_readb(base_addr + CySRER<<index));
+        printk(" CyCOR1 %x\n", cy_readb(base_addr + CyCOR1<<index));
+        printk(" CyCOR2 %x\n", cy_readb(base_addr + CyCOR2<<index));
+        printk(" CyCOR3 %x\n", cy_readb(base_addr + CyCOR3<<index));
+        printk(" CyCOR4 %x\n", cy_readb(base_addr + CyCOR4<<index));
+        printk(" CyCOR5 %x\n", cy_readb(base_addr + CyCOR5<<index));
+        printk(" CyCCSR %x\n", cy_readb(base_addr + CyCCSR<<index));
+        printk(" CyRDCR %x\n", cy_readb(base_addr + CyRDCR<<index));
+        printk(" CySCHR1 %x\n", cy_readb(base_addr + CySCHR1<<index));
+        printk(" CySCHR2 %x\n", cy_readb(base_addr + CySCHR2<<index));
+        printk(" CySCHR3 %x\n", cy_readb(base_addr + CySCHR3<<index));
+        printk(" CySCHR4 %x\n", cy_readb(base_addr + CySCHR4<<index));
+        printk(" CySCRL %x\n", cy_readb(base_addr + CySCRL<<index));
+        printk(" CySCRH %x\n", cy_readb(base_addr + CySCRH<<index));
+        printk(" CyLNC %x\n", cy_readb(base_addr + CyLNC<<index));
+        printk(" CyMCOR1 %x\n", cy_readb(base_addr + CyMCOR1<<index));
+        printk(" CyMCOR2 %x\n", cy_readb(base_addr + CyMCOR2<<index));
+        printk(" CyRTPR %x\n", cy_readb(base_addr + CyRTPR<<index));
+        printk(" CyMSVR1 %x\n", cy_readb(base_addr + CyMSVR1<<index));
+        printk(" CyMSVR2 %x\n", cy_readb(base_addr + CyMSVR2<<index));
+        printk(" CyRBPR %x\n", cy_readb(base_addr + CyRBPR<<index));
+        printk(" CyRCOR %x\n", cy_readb(base_addr + CyRCOR<<index));
+        printk(" CyTBPR %x\n", cy_readb(base_addr + CyTBPR<<index));
+        printk(" CyTCOR %x\n", cy_readb(base_addr + CyTCOR<<index));
 
     restore_flags(flags);
 } /* show_status */
diff -u --recursive --new-file v2.0.35/linux/drivers/char/ftape/ecc.c linux/drivers/char/ftape/ecc.c
--- v2.0.35/linux/drivers/char/ftape/ecc.c	Tue Dec  2 13:52:31 1997
+++ linux/drivers/char/ftape/ecc.c	Sun Nov 15 10:32:54 1998
@@ -80,7 +80,7 @@
  * Notice: to minimize the potential for confusion, we use r to
  *         denote the independent variable of the polynomials
  *         in the Galois Field GF(2^8).  We reserve x for polynomials
- *         that that have coefficients in GF(2^8).
+ *         that have coefficients in GF(2^8).
  *         
  * The Galois Field in which coefficient arithmetic is performed are
  * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible
diff -u --recursive --new-file v2.0.35/linux/drivers/char/isicom.c linux/drivers/char/isicom.c
--- v2.0.35/linux/drivers/char/isicom.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/isicom.c	Sun Nov 15 10:32:54 1998
@@ -0,0 +1,1981 @@
+/*
+ *	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.
+ *
+ *	Original driver code supplied by Multi-Tech
+ *
+ *	Changes
+ *	1/9/98	alan@redhat.com		Merge to 2.0.x kernel tree
+ *					Obtain and use official major/minors
+ *					Loader switched to a misc device
+ *					(fixed range check bug as a side effect)
+ *					Printk clean up
+ */
+
+/*
+ *		Currently ISICOM_BH is hard coded to 16 in isicom.h, cannot
+ *		ask the kernel for a free slot as of 2.0.x - sameer
+ */
+
+
+#include <linux/config.h> 
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/isicom.h>
+
+static int isicom_refcount = 0;
+static int prev_card = 3;	/*	start servicing isi_card[0]	*/
+static struct isi_board * irq_to_board[16] = { NULL, };
+static struct tty_driver isicom_normal, isicom_callout;
+static struct tty_struct * isicom_table[PORT_COUNT] = { NULL, };
+static struct termios * isicom_termios[PORT_COUNT] = { NULL, };
+static struct termios * isicom_termios_locked[PORT_COUNT] = { NULL, };
+
+static struct isi_board isi_card[BOARD_COUNT];
+static struct isi_port  isi_ports[PORT_COUNT];
+
+DECLARE_TASK_QUEUE(tq_isicom);
+
+static struct timer_list tx;
+static char re_schedule = 1;
+#ifdef ISICOM_DEBUG
+unsigned long tx_count = 0;
+#endif
+
+static int ISILoad_open(struct inode *inode, struct file *filp);
+static void ISILoad_release(struct inode *inode, struct file *filp);
+static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned  int cmd, unsigned long arg);
+
+static void isicom_tx(unsigned long _data);
+static void isicom_start(struct tty_struct * tty);
+
+static unsigned char * tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+/*   baud index mappings from linux defns to isi */
+
+static char linuxb_to_isib[] = {
+	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17,     
+	18, 19
+};
+
+/* 
+ *  Firmware loader driver specific routines
+ *
+ */
+
+static struct file_operations ISILoad_fops = {
+	NULL,	/*	lseek	*/
+	NULL,	/*	read	*/
+	NULL,	/*	write	*/
+	NULL,	/*	readdir	*/
+	NULL,	/*	select	*/
+	ISILoad_ioctl,
+	NULL,	/*	mmap	*/
+	ISILoad_open,
+	ISILoad_release,
+	NULL,	/*	fsync	*/
+	NULL,	/*	fasync	*/
+	NULL,	/*	check_media_change	*/
+	NULL,	/*	revalidate	*/
+};
+
+struct miscdevice isiloader_device = {
+	ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops
+};
+
+ 
+extern inline int WaitTillCardIsFree(unsigned short base)
+{
+	unsigned long count=0;
+	while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000));
+	if (inw(base+0xe)&0x1)  
+		return 0;
+	else
+		return 1;
+}
+
+static int ISILoad_open(struct inode *inode, struct file *filp)
+{
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISILoad:Firmware loader Opened!!!\n");
+#endif	
+	return 0;
+}
+
+static void ISILoad_release(struct inode *inode, struct file *filp)
+{
+#ifdef ISICOM_DEBUG
+	printk(KERN_DEBUG "ISILoad:Firmware loader Close(Release)d\n",);
+#endif	
+}
+
+static int ISILoad_ioctl(struct inode *inode, struct file *filp,
+		         unsigned int cmd, unsigned long arg)
+{
+	unsigned int card, i, j, signature, status;
+	unsigned short error, word_count, base;
+	bin_frame frame;
+	/* exec_record exec_rec; */
+	
+	/*	Added this check to avoid oopses on an ioctl with no
+	 *	args - sameer
+	 */ 
+	error=verify_area(VERIFY_READ, (void *) arg, sizeof(int));
+	if (error)
+		return error;
+	card=get_user((int *)arg);
+	if(card < 0 || card >= BOARD_COUNT)
+		return -ENXIO;
+		
+	base=isi_card[card].base;
+	
+	if(base==0)
+		return -ENXIO;	/* disabled or not used */
+	
+	switch(cmd) {
+		case MIOCTL_RESET_CARD:
+			if (!suser())
+				return -EPERM;
+			error=verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+			if (error)
+				return error;
+				
+			printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%x ",card+1,base);
+								
+			inw(base+0x8);
+			
+			for(i=jiffies+HZ/100;i>jiffies;);
+				
+			outw(0,base+0x8); /* Reset */
+			
+			for(j=1;j<=3;j++) {
+				for(i=jiffies+HZ;i>jiffies;);
+				printk(".");
+			}	
+			signature=(inw(base+0x4)) & 0xff;	
+				
+			if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) {
+#ifdef ISICOM_DEBUG				
+				printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
+#endif				
+				printk("\nISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
+				return -EIO;					
+			}
+				
+			switch(signature) {
+			case	0xa5:
+			case	0xbb:
+			case	0xdd:	isi_card[card].port_count = 8;
+				     	isi_card[card].shift_count = 12;
+				     	break;
+				        
+			case	0xcc:	isi_card[card].port_count = 16;
+					isi_card[card].shift_count = 11;
+					break;  			
+					
+			default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
+#ifdef ISICOM_DEBUG			
+				 printk("Sig=0x%x\n",signature);
+#endif				 
+				 return -EIO;
+			}
+			printk("-Done\n");
+			put_user(signature,(unsigned int*)arg);
+			return 0;
+						
+	case	MIOCTL_LOAD_FIRMWARE:
+			if (!suser())
+				return -EPERM;
+			error=verify_area(VERIFY_READ, (void *) arg, sizeof(bin_frame));
+			if (error)
+				return error;
+				
+			memcpy_fromfs(&frame, (void *) arg, sizeof(bin_frame));
+			
+			if (WaitTillCardIsFree(base))
+				return -EIO;
+			
+			outw(0xf0,base);	/* start upload sequence */ 
+			outw(0x00,base);
+			outw((frame.addr), base);/*      lsb of adderess    */
+			
+			word_count=(frame.count >> 1) + frame.count % 2;
+			outw(word_count, base);
+			InterruptTheCard(base);
+			
+			for(i=0;i<=0x2f;i++);	/* a wee bit of delay */
+			
+			if (WaitTillCardIsFree(base)) 
+				return -EIO;
+				
+			if ((status=inw(base+0x4))!=0) {
+				printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", 
+				card+1, frame.addr, frame.count, status);
+				return -EIO;
+			}
+			outsw(base, (void *) frame.bin_data, word_count);
+			
+			InterruptTheCard(base);
+			
+			for(i=0;i<=0x0f;i++);	/* another wee bit of delay */ 
+			
+			if (WaitTillCardIsFree(base)) 
+				return -EIO;
+				
+			if ((status=inw(base+0x4))!=0) {
+				printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status);
+				return -EIO;
+			}	
+			return 0;
+						
+	case	MIOCTL_READ_FIRMWARE:
+			if (!suser())
+				return -EPERM;
+			error=verify_area(VERIFY_READ, (void *) arg, sizeof(bin_header));
+			if (error)
+				return error;
+				
+			memcpy_fromfs(&frame, (void *) arg, sizeof(bin_header));
+			
+			if (WaitTillCardIsFree(base))
+				return -EIO;
+			
+			outw(0xf1,base);	/* start download sequence */ 
+			outw(0x00,base);
+			outw((frame.addr), base);/*      lsb of adderess    */
+			
+			word_count=(frame.count >> 1) + frame.count % 2;
+			outw(word_count+1, base);
+			InterruptTheCard(base);
+			
+			for(i=0;i<=0xf;i++);	/* a wee bit of delay */
+			
+			if (WaitTillCardIsFree(base)) 
+				return -EIO;
+				
+			if ((status=inw(base+0x4))!=0) {
+				printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", 
+				card+1, frame.addr, frame.count, status);
+				return -EIO;
+			}
+			
+			inw(base);
+			insw(base, frame.bin_data, word_count);
+			InterruptTheCard(base);
+			
+			for(i=0;i<=0x0f;i++);	/* another wee bit of delay */ 
+			
+			if (WaitTillCardIsFree(base)) 
+				return -EIO;
+				
+			if ((status=inw(base+0x4))!=0) {
+				printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status);
+				return -EIO;
+			}	
+			error=verify_area(VERIFY_WRITE, (void *) arg, sizeof(bin_frame));
+			if (error)
+				return error;
+			memcpy_tofs((void *) arg, &frame, sizeof(bin_frame));	
+			
+			return 0;
+	
+	case	MIOCTL_XFER_CTRL:
+			if (!suser())
+				return -EPERM;
+			if (WaitTillCardIsFree(base)) 
+				return -EIO;
+					
+			outw(0xf2, base);
+			outw(0x800, base);
+			outw(0x0, base);
+			outw(0x0, base);
+			InterruptTheCard(base);
+			
+			isi_card[card].status |= FIRMWARE_LOADED;
+			return 0;	
+			
+	default:
+#ifdef ISICOM_DEBUG	
+		printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd); 
+#endif
+		return -ENOIOCTLCMD;
+	
+	}
+	
+}
+		        	
+
+/*
+ *	ISICOM Driver specific routines ...
+ *
+ */
+ 
+static inline int isicom_paranoia_check(struct isi_port const * port, kdev_t dev, 
+					const char * routine)
+{
+#ifdef ISICOM_DEBUG 
+	static const char * badmagic = 
+			KERN_WARNING "ISICOM: Warning: bad isicom magic for dev %s in %s.\n";
+	static const char * badport = 
+			KERN_WARNING "ISICOM: Warning: NULL isicom port for dev %s in %s.\n";		
+	if (!port) {
+		printk(badport, kdevname(dev), routine);
+		return 1;
+	}
+	if (port->magic != ISICOM_MAGIC) {
+		printk(badmagic, kdevname(dev), routine);
+		return 1;
+	}	
+#endif	
+	return 0;
+}
+			
+extern inline void schedule_bh(struct isi_port * port)
+{
+	queue_task_irq_off(&port->bh_tqueue, &tq_isicom);
+	mark_bh(ISICOM_BH);
+} 
+
+/*	Transmitter	*/
+
+static void isicom_tx(unsigned long _data)
+{
+	short count = (BOARD_COUNT-1), card, base;
+	short txcount, wait, wrd, residue, word_count, cnt;
+	struct isi_port * port;
+	struct tty_struct * tty;
+	unsigned long flags;
+	
+#ifdef ISICOM_DEBUG
+	++tx_count;
+#endif	
+	
+	/*	find next active board	*/
+	card = (prev_card + 1) & 0x0003;
+	while(count-- > 0) {
+		if (isi_card[card].status & BOARD_ACTIVE) 
+			break;
+		card = (card + 1) & 0x0003;	
+	}
+	if (!(isi_card[card].status & BOARD_ACTIVE))
+		goto sched_again;
+		
+	prev_card = card;
+	
+	count = isi_card[card].port_count;
+	port = isi_card[card].ports;
+	base = isi_card[card].base;
+	for (;count > 0;count--, port++) {
+		/* port not active or tx disabled to force flow control */
+		if (!(port->status & ISI_TXOK))
+			continue;
+		
+		tty = port->tty;
+		save_flags(flags); cli();
+		txcount = MIN(TX_SIZE, port->xmit_cnt);
+		if ((txcount <= 0) || tty->stopped || tty->hw_stopped) {
+			restore_flags(flags);
+			continue;
+		}
+		wait = 200;	
+		while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+		if (wait <= 0) {
+			restore_flags(flags);
+#ifdef ISICOM_DEBUG
+			printk(KERN_DEBUG "ISICOM: isicom_tx:Card(0x%x) found busy.\n",
+				card);
+#endif
+			continue;
+		}
+		if (!(inw(base + 0x02) & (1 << port->channel))) {
+			restore_flags(flags);
+#ifdef ISICOM_DEBUG					
+			printk(KERN_DEBUG "ISICOM: isicom_tx: cannot tx to 0x%x:%d.\n",
+					base, port->channel + 1);
+#endif					
+			continue;		
+		}
+#ifdef ISICOM_DEBUG
+		printk(KERN_DEBUG "ISICOM: txing %d bytes, port%d.\n", 
+				txcount, port->channel+1); 
+#endif	
+		outw((port->channel << isi_card[card].shift_count) | txcount
+					, base);
+		residue = NO;
+		wrd = 0;			
+		while (1) {
+			cnt = MIN(txcount, (SERIAL_XMIT_SIZE - port->xmit_tail));
+			if (residue == YES) {
+				residue = NO;
+				if (cnt > 0) {
+					wrd |= (port->xmit_buf[port->xmit_tail] << 8);
+					port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+					port->xmit_cnt--;
+					txcount--;
+					cnt--;
+					outw(wrd, base);			
+				}
+				else {
+					outw(wrd, base);
+					break;
+				}
+			}		
+			if (cnt <= 0) break;
+			word_count = cnt >> 1;
+			outsw(base, port->xmit_buf+port->xmit_tail, word_count);
+			port->xmit_tail = (port->xmit_tail + (word_count << 1)) &
+						(SERIAL_XMIT_SIZE - 1);
+			txcount -= (word_count << 1);
+			port->xmit_cnt -= (word_count << 1);
+			if (cnt & 0x0001) {
+				residue = YES;
+				wrd = port->xmit_buf[port->xmit_tail];
+				port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+				port->xmit_cnt--;
+				txcount--;
+			}
+		}
+/*
+ *	Replaced the code below with hopefully a faster loop - sameer
+ */
+
+/*					
+		while (1) {
+			wrd = port->xmit_buf[port->xmit_tail++];
+			port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1);
+			port->xmit_cnt--;
+			if (--txcount > 0) {
+				wrd |= (port->xmit_buf[port->xmit_tail++] << 8);
+				port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1);
+				port->xmit_cnt--;
+				outw(wrd, base);
+				if (--txcount <= 0) break;
+			}
+			else {
+				outw(wrd, base);
+				break;
+			}
+		}
+*/
+		InterruptTheCard(base);
+		if (port->xmit_cnt <= 0)
+			port->status &= ~ISI_TXOK;
+		if (port->xmit_cnt <= WAKEUP_CHARS)
+			schedule_bh(port);
+		restore_flags(flags);
+	}	
+
+		/*	schedule another tx for hopefully in about 10ms	*/	
+sched_again:	
+	if (!re_schedule)	
+		return;
+	init_timer(&tx);
+	tx.expires = jiffies + HZ/100;
+	tx.data = 0;
+	tx.function = isicom_tx;
+	add_timer(&tx);
+	
+	return;	
+}		
+ 
+ 		/* 	Interrupt handlers 	*/
+static void do_isicom_bh(void)
+{
+	run_task_queue(&tq_isicom);
+}
+
+
+ 
+static void isicom_bottomhalf(void * data)
+{
+	struct isi_port * port = (struct isi_port *) data;
+	struct tty_struct * tty = port->tty;
+	
+	if (!tty)
+		return;
+	
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	wake_up_interruptible(&tty->write_wait);
+} 		
+ 		
+/* main interrupt handler routine */ 		
+static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+	struct isi_board * card;
+	struct isi_port * port;
+	struct tty_struct * tty;
+	unsigned short base, header, word_count, count;
+	unsigned char channel;
+	short byte_count;
+	
+	card = irq_to_board[irq];
+	if (!card || !(card->status & FIRMWARE_LOADED)) {
+		printk(KERN_DEBUG "ISICOM: interrupt: not handling irq%d!.\n", irq);
+		return;
+	}
+	base = card->base;
+	
+	inw(base);		/* get the dummy word out */
+	header = inw(base);
+	channel = (header & 0x7800) >> card->shift_count;
+	byte_count = header & 0xff;
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM:Intr:(0x%x:%d).\n", base, channel+1);
+#endif	
+	if ((channel+1) > card->port_count) {
+		printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%x): %d(channel) > port_count.\n",
+				base, channel+1);
+		ClearInterrupt(base);		
+		return;			
+	}
+	port = card->ports + channel;
+	if (!(port->flags & ASYNC_INITIALIZED)) {
+		ClearInterrupt(base);
+		return;
+	}	
+		
+	tty = port->tty;
+	
+	if (header & 0x8000) {		/* Status Packet */
+		header = inw(base);
+		switch(header & 0xff) {
+			case 0:	/* Change in EIA signals */
+				
+				if (port->flags & ASYNC_CHECK_CD) {
+					if (port->status & ISI_DCD) {
+						if (!(header & ISI_DCD)) {
+						/* Carrier has been lost  */
+#ifdef ISICOM_DEBUG						
+							printk(KERN_DEBUG "ISICOM: interrupt: DCD->low.\n");
+#endif							
+							port->status &= ~ISI_DCD;
+							if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+								(port->flags & ASYNC_CALLOUT_NOHUP)))
+								queue_task_irq_off(&port->hangup_tq,
+									&tq_scheduler);
+						}
+					}
+					else {
+						if (header & ISI_DCD) {
+						/* Carrier has been detected */
+#ifdef ISICOM_DEBUG
+							printk(KERN_DEBUG "ISICOM: interrupt: DCD->high.\n");
+#endif							
+							port->status |= ISI_DCD;
+							wake_up_interruptible(&port->open_wait);
+						}
+					}
+				}
+				else {
+					if (header & ISI_DCD) 
+						port->status |= ISI_DCD;
+					else
+						port->status &= ~ISI_DCD;
+				}	
+				
+				if (port->flags & ASYNC_CTS_FLOW) {
+					if (port->tty->hw_stopped) {
+						if (header & ISI_CTS) {
+							port->tty->hw_stopped = 0;
+							/* start tx ing */
+							port->status |= (ISI_TXOK | ISI_CTS);
+							schedule_bh(port);
+						}
+					}
+					else {
+						if (!(header & ISI_CTS)) {
+							port->tty->hw_stopped = 1;
+							/* stop tx ing */
+							port->status &= ~(ISI_TXOK | ISI_CTS);
+						}
+					}
+				}
+				else {
+					if (header & ISI_CTS) 
+						port->status |= ISI_CTS;
+					else
+						port->status &= ~ISI_CTS;
+				}
+				
+				if (header & ISI_DSR) 
+					port->status |= ISI_DSR;
+				else
+					port->status &= ~ISI_DSR;
+				
+				if (header & ISI_RI) 
+					port->status |= ISI_RI;
+				else
+					port->status &= ~ISI_RI;						
+				
+				break;
+				
+			case 1:	/* Received Break !!!	 */
+				if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+					break;
+				*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+				/* dunno if this is right */	
+				*tty->flip.char_buf_ptr++ = 0;
+				tty->flip.count++;
+				if (port->flags & ASYNC_SAK)
+					do_SAK(tty);
+				queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+				break;
+				
+			case 2:	/* Statistics		 */
+				printk(KERN_DEBUG "ISICOM: isicom_interrupt: stats!!!.\n");			
+				break;
+				
+			default:
+				printk(KERN_WARNING "ISICOM: Intr: Unknown code in status packet.\n");
+				break;
+		}	 
+	}
+	else {				/* Data   Packet */
+		count = MIN(byte_count, (TTY_FLIPBUF_SIZE - tty->flip.count));
+#ifdef ISICOM_DEBUG
+		printk(KERN_DEBUG "ISICOM: Intr: Can rx %d of %d bytes.\n", 
+					count, byte_count);
+#endif			
+		word_count = count >> 1;
+		insw(base, tty->flip.char_buf_ptr, word_count);
+		tty->flip.char_buf_ptr += (word_count << 1);		
+		byte_count -= (word_count << 1);
+		if (count & 0x0001) {
+			*tty->flip.char_buf_ptr++ = (char)(inw(base) & 0xff);
+			byte_count -= 2;
+		}	
+		memset(tty->flip.flag_buf_ptr, 0, count);
+		tty->flip.flag_buf_ptr += count;
+		tty->flip.count += count;
+		
+		if (byte_count > 0) {
+			printk(KERN_DEBUG "ISICOM: Intr(0x%x:%d): Flip buffer overflow! dropping bytes...\n",
+					base, channel+1);
+			while(byte_count > 0) { /* drain out unread xtra data */
+				inw(base);
+				byte_count -= 2;
+			}
+		}
+		queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+	}
+	ClearInterrupt(base);
+	return;
+} 
+
+ /* called with interrupts disabled */ 
+static void isicom_config_port(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	struct tty_struct * tty;
+	unsigned long baud;
+	unsigned short channel_setup, wait, base = card->base;
+	unsigned short channel = port->channel, shift_count = card->shift_count;
+	unsigned char flow_ctrl;
+	
+	if (!(tty = port->tty) || !tty->termios)
+		return;
+	baud = C_BAUD(tty);
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		
+		/*  if CBAUDEX bit is on and the baud is set to either 50 or 75
+		 *  then the card is programmed for 57.6Kbps or 115Kbps
+		 *  respectively.
+		 */   
+		 
+		if (baud < 1 || baud > 2)
+			port->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}	
+	if (baud == 15) {
+	
+		/*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set 
+		 *  by the set_serial_info ioctl ... this is done by
+		 *  the 'setserial' utility.
+		 */  
+			
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baud++;     /*  57.6 Kbps */
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baud +=2;   /*  115  Kbps */	 
+	}
+	if (linuxb_to_isib[baud] == -1) {
+		/* hang up */
+	 	drop_dtr(port);
+	 	return;
+	}	
+	else  
+		raise_dtr(port);
+		
+	wait = 100;	
+	while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));	
+	if (!wait) {
+		printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at channel setup.\n");
+		return;
+	}			 
+	outw(0x8000 | (channel << shift_count) |0x03, base);
+	outw(linuxb_to_isib[baud] << 8 | 0x03, base);
+	channel_setup = 0;
+	switch(C_CSIZE(tty)) {
+		case CS5:
+			channel_setup |= ISICOM_CS5;
+			break;
+		case CS6:
+			channel_setup |= ISICOM_CS6;
+			break;
+		case CS7:
+			channel_setup |= ISICOM_CS7;
+			break;
+		case CS8:
+			channel_setup |= ISICOM_CS8;
+			break;
+	}
+		
+	if (C_CSTOPB(tty))
+		channel_setup |= ISICOM_2SB;
+	
+	if (C_PARENB(tty))
+		channel_setup |= ISICOM_EVPAR;
+	if (C_PARODD(tty))
+		channel_setup |= ISICOM_ODPAR;	
+	outw(channel_setup, base);	
+	InterruptTheCard(base);
+	
+	if (C_CLOCAL(tty))
+		port->flags &= ~ASYNC_CHECK_CD;
+	else
+		port->flags |= ASYNC_CHECK_CD;	
+	
+	/* flow control settings ...*/
+	flow_ctrl = 0;
+	port->flags &= ~ASYNC_CTS_FLOW;
+	if (C_CRTSCTS(tty)) {
+		port->flags |= ASYNC_CTS_FLOW;
+		flow_ctrl |= ISICOM_CTSRTS;
+	}	
+	if (I_IXON(tty))	
+		flow_ctrl |= ISICOM_RESPOND_XONXOFF;
+	if (I_IXOFF(tty))
+		flow_ctrl |= ISICOM_INITIATE_XONXOFF;	
+		
+	wait = 100;	
+	while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));	
+	if (!wait) {
+		printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at flow setup.\n");
+		return;
+	}			 
+	outw(0x8000 | (channel << shift_count) |0x04, base);
+	outw(flow_ctrl << 8 | 0x05, base);
+	outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
+	InterruptTheCard(base);
+	
+	/*	rx enabled -> enable port for rx on the card	*/
+	if (C_CREAD(tty)) {
+		card->port_status |= (1 << channel);
+		outw(card->port_status, base + 0x02);
+	}
+		
+}
+ 
+/* open et all */ 
+
+extern inline void isicom_setup_board(struct isi_board * bp)
+{
+	int channel;
+	struct isi_port * port;
+	unsigned long flags;
+	
+	if (bp->status & BOARD_ACTIVE) 
+		return;
+	port = bp->ports;
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts start, port_count %d...\n", bp->port_count);
+#endif
+	for(channel = 0; channel < bp->port_count; channel++, port++) {
+		save_flags(flags); cli();
+		drop_dtr_rts(port);
+		restore_flags(flags);
+	}
+#ifdef ISICOM_DEBUG		
+	printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts stop...\n");	
+#endif	
+	
+	bp->status |= BOARD_ACTIVE;
+	MOD_INC_USE_COUNT;
+	return;
+}
+ 
+static int isicom_setup_port(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	unsigned long flags;
+	
+	if (port->flags & ASYNC_INITIALIZED)
+		return 0;
+	if (!port->xmit_buf) {
+		unsigned long page;
+		
+		if (!(page = get_free_page(GFP_KERNEL)))
+			return -ENOMEM;
+		
+		if (port->xmit_buf) {
+			free_page(page);
+			return -ERESTARTSYS;
+		}
+		port->xmit_buf = (unsigned char *) page;	
+	}	
+	save_flags(flags); cli();
+	if (port->tty)
+		clear_bit(TTY_IO_ERROR, &port->tty->flags);
+	if (port->count == 1)
+		card->count++;
+		
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	
+	/*	discard any residual data	*/
+	kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX);
+	
+	isicom_config_port(port);
+	port->flags |= ASYNC_INITIALIZED;
+	
+	restore_flags(flags);
+	
+	return 0;		
+} 
+ 
+static int block_til_ready(struct tty_struct * tty, struct file * filp, struct isi_port * port) 
+{
+	int do_clocal = 0, retval;
+	struct wait_queue wait = { current, NULL };
+
+	/* block if port is in the process of being closed */
+
+	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+#ifdef ISICOM_DEBUG	
+		printk(KERN_DEBUG "ISICOM: block_til_ready: close in progress.\n");
+#endif		
+		interruptible_sleep_on(&port->close_wait);
+		if (port->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+	}
+	
+	/* trying to open a callout device... check for constraints */
+	
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+#ifdef ISICOM_DEBUG
+		printk(KERN_DEBUG "ISICOM: bl_ti_rdy: callout open.\n");	
+#endif		
+		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;
+		cli();
+		raise_dtr_rts(port);
+		sti();
+		return 0;
+	}
+	
+	/* if non-blocking mode is set ... */
+	
+	if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) {
+#ifdef ISICOM_DEBUG	
+		printk(KERN_DEBUG "ISICOM: block_til_ready: non-block mode.\n");
+#endif		
+		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;
+	}
+#ifdef ISICOM_DEBUG	
+	if (do_clocal)
+		printk(KERN_DEBUG "ISICOM: block_til_ready: CLOCAL set.\n");
+#endif 		
+	
+	/* block waiting for DCD to be asserted, and while 
+						callout dev is busy */
+	retval = 0;
+	add_wait_queue(&port->open_wait, &wait);
+	cli();
+		if (!tty_hung_up_p(filp))
+			port->count--;
+	sti();
+	port->blocked_open++;
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: block_til_ready: waiting for DCD...\n");
+#endif	
+	while (1) {
+		cli();
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) 
+			raise_dtr_rts(port);
+		
+		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;
+#ifdef ISICOM_DEBUG				
+			printk(KERN_DEBUG "ISICOM: block_til_ready: tty_hung_up_p || not init.\n"); 
+#endif			
+			break;
+		}	
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(port->flags & ASYNC_CLOSING) &&
+		    (do_clocal || (port->status & ISI_DCD))) {
+#ifdef ISICOM_DEBUG		    
+		 	printk(KERN_DEBUG "ISICOM: block_til_ready: do_clocal || DCD.\n");   
+#endif		 	
+			break;
+		}	
+		if (current->signal & ~current->blocked) {
+#ifdef ISICOM_DEBUG		
+			printk(KERN_DEBUG "ISICOM: block_til_ready: sig blocked.\n");
+#endif			
+			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 isicom_open(struct tty_struct * tty, struct file * filp)
+{
+	struct isi_port * port;
+	struct isi_board * card;
+	unsigned int line, board;
+	unsigned long flags;
+	int error;
+
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: open start!!!.\n");
+#endif	
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "line = %d.\n", line);
+#endif	
+	
+	if ((line < 0) || (line > (PORT_COUNT-1)))
+		return -ENODEV;
+	board = BOARD(line);
+	
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "board = %d.\n", board);
+#endif	
+	
+	card = &isi_card[board];
+	if (!(card->status & FIRMWARE_LOADED)) {
+#ifdef ISICOM_DEBUG	
+		printk(KERN_DEBUG"ISICOM: Firmware not loaded to card%d.\n", board);
+#endif		
+		return -ENODEV;
+	}
+	
+	/*  open on higher 8 dev files on a 8 port card !!! */
+	if (card->port_count == 8) 
+		if (line > ((board * 16)+7)) {
+			printk(KERN_ERR "ISICOM: Opened >8 on a 8 port card.\n");
+			return -ENODEV;
+		}	
+	port = &isi_ports[line];	
+	if (isicom_paranoia_check(port, tty->device, "isicom_open"))
+		return -ENODEV;
+		
+#ifdef ISICOM_DEBUG		
+	printk(KERN_DEBUG "ISICOM: isicom_setup_board ...\n");		
+#endif	
+	isicom_setup_board(card);		
+	
+	port->count++;
+	tty->driver_data = port;
+	port->tty = tty;
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: isicom_setup_port ...\n");
+#endif	
+	if ((error = isicom_setup_port(port))!=0)
+		return error;
+#ifdef ISICOM_DEBUG		
+	printk(KERN_DEBUG "ISICOM: block_til_ready ...\n");	
+#endif	
+	if ((error = block_til_ready(tty, filp, port))!=0)
+		return error;
+		
+	if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = port->normal_termios;
+		else 
+			*tty->termios = port->callout_termios;
+		save_flags(flags); cli();
+		isicom_config_port(port);
+		restore_flags(flags);		
+	}	
+	
+	port->session = current->session;	
+	port->pgrp = current->pgrp;
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: open end!!!.\n");
+#endif	
+	return 0;      		
+}
+ 
+/* close et all */
+
+extern inline void isicom_shutdown_board(struct isi_board * bp)
+{
+	int channel;
+	struct isi_port * port;
+	
+	if (!(bp->status & BOARD_ACTIVE))
+		return;
+	bp->status &= ~BOARD_ACTIVE;
+	port = bp->ports;
+	for(channel = 0; channel < bp->port_count; channel++, port++) {
+		drop_dtr_rts(port);
+	}	
+	MOD_DEC_USE_COUNT;
+}
+
+static void isicom_shutdown_port(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	struct tty_struct * tty;	
+	
+	if (!(port->flags & ASYNC_INITIALIZED))
+		return;
+	if (port->xmit_buf) {
+		free_page((unsigned long) port->xmit_buf);
+		port->xmit_buf = NULL;
+	}	
+	if (!(tty = port->tty) || C_HUPCL(tty)) 
+		/* drop dtr on this port */
+		drop_dtr(port);
+		
+	/* any other port uninits  */ 
+	
+	if (tty)
+		set_bit(TTY_IO_ERROR, &tty->flags);
+	port->flags &= ~ASYNC_INITIALIZED;
+	
+	if (--card->count < 0) {
+		printk(KERN_DEBUG "ISICOM: isicom_shutdown_port: bad board(0x%x) count %d.\n",
+			card->base, card->count);
+		card->count = 0;	
+	}
+	
+	/* last port was closed , shutdown that boad too */
+	if (!card->count)
+		isicom_shutdown_board(card);
+}
+
+static void isicom_close(struct tty_struct * tty, struct file * filp)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	struct isi_board * card = port->card;
+	unsigned long flags;
+	
+	if (!port)
+		return;
+	if (isicom_paranoia_check(port, tty->device, "isicom_close"))
+		return;
+	
+#ifdef ISICOM_DEBUG		
+	printk(KERN_DEBUG "ISICOM: Close start!!!.\n");
+#endif	
+	
+	save_flags(flags); cli();
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+	
+	if ((tty->count == 1) && (port->count != 1)) {
+		printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count"
+			"tty->count = 1	port count = %d.\n",
+			card->base, port->count);
+		port->count = 1;
+	}
+	if (--port->count < 0) {
+		printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count for"
+			"channel%d = %d", card->base, port->channel, 
+			port->count);
+		port->count = 0;	
+	}
+	
+	if (port->count) {
+		restore_flags(flags);
+		return;
+	} 	
+	port->flags |= ASYNC_CLOSING;
+	/* 
+	 * save termios struct since callout and dialin termios may be 
+	 * different.
+	 */	
+	if (port->flags & ASYNC_NORMAL_ACTIVE)
+		port->normal_termios = *tty->termios;
+	if (port->flags & ASYNC_CALLOUT_ACTIVE)
+		port->callout_termios = *tty->termios;
+	
+	tty->closing = 1;
+	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, port->closing_wait);
+	/* indicate to the card that no more data can be received 
+	   on this port */
+	if (port->flags & ASYNC_INITIALIZED) {   
+		card->port_status &= ~(1 << port->channel);
+		outw(card->port_status, card->base + 0x02);
+	}	
+	isicom_shutdown_port(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->tty = 0;
+	if (port->blocked_open) {
+		if (port->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + port->close_delay;
+#ifdef ISICOM_DEBUG			
+			printk(KERN_DEBUG "ISICOM: scheduling until time out.\n");
+#endif			
+			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);
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: Close end!!!.\n");
+#endif	
+}
+
+/* write et all */
+static int isicom_write(struct tty_struct * tty, int from_user,
+			const unsigned char * buf, int count)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	unsigned long flags;
+	int cnt, total = 0;
+#ifdef ISICOM_DEBUG
+	printk(KERN_DEBUG "ISICOM: isicom_write for port%d: %d bytes.\n",
+			port->channel+1, count);
+#endif	  	
+	if (isicom_paranoia_check(port, tty->device, "isicom_write"))
+		return 0;
+	
+	if (!tty || !port->xmit_buf || !tmp_buf)
+		return 0;
+	if (from_user)
+		down(&tmp_buf_sem); /* acquire xclusive access to tmp_buf */
+		
+	save_flags(flags);
+	while(1) {	
+		cli();
+		cnt = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+			SERIAL_XMIT_SIZE - port->xmit_head));
+		if (cnt <= 0) 
+			break;
+		
+		if (from_user) {
+			/* the following may block for paging... hence 
+			   enabling interrupts but tx routine may have 
+			   created more space in xmit_buf when the ctrl 
+			   gets back here 
+			sti(); */
+			memcpy_fromfs(tmp_buf, buf, cnt);
+/*			cli();*/
+			cnt = MIN(cnt, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+			SERIAL_XMIT_SIZE - port->xmit_head));
+			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, cnt);
+		}	
+		else
+			memcpy(port->xmit_buf + port->xmit_head, buf, cnt);
+		port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE - 1);
+		port->xmit_cnt += cnt;
+		restore_flags(flags);
+		buf += cnt;
+		count -= cnt;
+		total += cnt;
+	}		
+	if (from_user)
+		up(&tmp_buf_sem);
+	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
+		port->status |= ISI_TXOK;
+	restore_flags(flags);
+#ifdef ISICOM_DEBUG
+	printk(KERN_DEBUG "ISICOM: isicom_write %d bytes written.\n", total);
+#endif		
+	return total;	
+}
+
+/* put_char et all */
+static void isicom_put_char(struct tty_struct * tty, unsigned char ch)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	unsigned long flags;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_put_char"))
+		return;
+	
+	if (!tty || !port->xmit_buf)
+		return;
+#ifdef ISICOM_DEBUG
+	printk(KERN_DEBUG "ISICOM: put_char, port %d, char %c.\n", port->channel+1, ch);
+#endif			
+		
+	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);
+}
+
+/* flush_chars et all */
+static void isicom_flush_chars(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_flush_chars"))
+		return;
+	
+	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !port->xmit_buf)
+		return;
+		
+	/* this tells the transmitter to consider this port for
+	   data output to the card ... that's the best we can do. */
+	port->status |= ISI_TXOK;	
+}
+
+/* write_room et all */
+static int isicom_write_room(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	int free;
+	if (isicom_paranoia_check(port, tty->device, "isicom_write_room"))
+		return 0;
+	
+	free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+	if (free < 0)
+		free = 0;
+	return free;
+}
+
+/* chars_in_buffer et all */
+static int isicom_chars_in_buffer(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	if (isicom_paranoia_check(port, tty->device, "isicom_chars_in_buffer"))
+		return 0;
+	return port->xmit_cnt;
+}
+
+/* ioctl et all */
+extern inline void isicom_send_break(struct isi_port * port, unsigned long length)
+{
+	struct isi_board * card = port->card;
+	short wait = 10;
+	unsigned short base = card->base;	
+	unsigned long flags;
+	
+	save_flags(flags); cli();
+	while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));	
+	if (!wait) {
+		printk(KERN_DEBUG "ISICOM: Card found busy in isicom_send_break.\n");
+		return;
+	}	
+	outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
+	outw((length & 0xff) << 8 | 0x00, base);
+	outw((length & 0xff00), base);
+	InterruptTheCard(base);
+	restore_flags(flags);
+}
+
+static int isicom_get_modem_info(struct isi_port * port, unsigned int * value)
+{
+	/* just send the port status */
+	unsigned int info;
+	unsigned short status = port->status;
+	
+	info =  ((status & ISI_RTS) ? TIOCM_RTS : 0) |
+		((status & ISI_DTR) ? TIOCM_DTR : 0) |
+		((status & ISI_DCD) ? TIOCM_CAR : 0) |
+		((status & ISI_DSR) ? TIOCM_DSR : 0) |
+		((status & ISI_CTS) ? TIOCM_CTS : 0) |
+		((status & ISI_RI ) ? TIOCM_RI  : 0);
+	put_user(info, (unsigned long *) value);
+	return 0;	
+}
+
+static int isicom_set_modem_info(struct isi_port * port, unsigned int cmd,
+					unsigned int * value)
+{
+	unsigned int arg;
+	unsigned long flags;
+	
+	arg = get_user(value);
+	save_flags(flags); cli();
+	switch(cmd) {
+		case TIOCMBIS:
+			if (arg & TIOCM_RTS) 
+				raise_rts(port);
+			if (arg & TIOCM_DTR) 
+				raise_dtr(port);
+			break;
+		
+		case TIOCMBIC:
+			if (arg & TIOCM_RTS)
+				drop_rts(port);
+			if (arg & TIOCM_DTR)
+				drop_dtr(port);	
+			break;
+			
+		case TIOCMSET:
+			if (arg & TIOCM_RTS)
+				raise_rts(port);
+			else
+				drop_rts(port);
+			
+			if (arg & TIOCM_DTR)
+				raise_dtr(port);
+			else
+				drop_dtr(port);
+			break;
+		
+		default:
+			restore_flags(flags);
+			return -EINVAL;		 	
+	}
+	restore_flags(flags);
+	return 0;
+}			
+
+static int isicom_set_serial_info(struct isi_port * port,
+					struct serial_struct * info)
+{
+	struct serial_struct newinfo;
+	unsigned long flags;
+	int reconfig_port;
+
+	memcpy_fromfs(&newinfo, info, sizeof(newinfo));
+	reconfig_port = ((port->flags & ASYNC_SPD_MASK) != 
+			 (newinfo.flags & ASYNC_SPD_MASK));
+	
+	if (!suser()) {
+		if ((newinfo.close_delay != port->close_delay) ||
+		    (newinfo.closing_wait != port->closing_wait) ||
+		    ((newinfo.flags & ~ASYNC_USR_MASK) != 
+		     (port->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		port->flags = ((port->flags & ~ ASYNC_USR_MASK) |
+				(newinfo.flags & ASYNC_USR_MASK));
+	}	
+	else {
+		port->close_delay = newinfo.close_delay;
+		port->closing_wait = newinfo.closing_wait; 
+		port->flags = ((port->flags & ~ASYNC_FLAGS) | 
+				(newinfo.flags & ASYNC_FLAGS));
+	}
+	if (reconfig_port) {
+		save_flags(flags); cli();
+		isicom_config_port(port);
+		restore_flags(flags);
+	}
+	return 0;		 
+}		
+
+static int isicom_get_serial_info(struct isi_port * port, 
+					struct serial_struct * info)
+{
+	struct serial_struct out_info;
+	
+	memset(&out_info, 0, sizeof(out_info));
+/*	out_info.type = ? */
+	out_info.line = port - isi_ports;
+	out_info.port = port->card->base;
+	out_info.irq = port->card->irq;
+	out_info.flags = port->flags;
+/*	out_info.baud_base = ? */
+	out_info.close_delay = port->close_delay;
+	out_info.closing_wait = port->closing_wait;
+	memcpy_tofs(info, &out_info, sizeof(out_info));
+	return 0;
+}					
+
+static int isicom_ioctl(struct tty_struct * tty, struct file * filp,
+			unsigned int cmd, unsigned long arg) 
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	int retval, error;
+
+	if (isicom_paranoia_check(port, tty->device, "isicom_ioctl"))
+		return -ENODEV;
+
+	switch(cmd) {
+		case TCSBRK:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (!arg)
+				isicom_send_break(port, HZ/4);
+			return 0;
+			
+		case TCSBRKP:	
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			isicom_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:
+			error = verify_area(VERIFY_READ, (void *) arg, sizeof(long)); 	
+			if (error)
+				return error;
+			arg = get_user((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 isicom_get_modem_info(port, (unsigned int*) arg);
+			
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET: 	
+			error = verify_area(VERIFY_READ, (void *) arg, 
+					sizeof(unsigned int)); 		   
+			if (error)
+				return error;
+			return isicom_set_modem_info(port, cmd, 
+					(unsigned int *) arg);
+		
+		case TIOCGSERIAL:
+			error = verify_area(VERIFY_WRITE, (void *) arg, 
+					sizeof(struct serial_struct));
+			if (error)
+				return error;		
+			return isicom_get_serial_info(port, 
+					(struct serial_struct *) arg);
+		
+		case TIOCSSERIAL:
+			error = verify_area(VERIFY_READ, (void *) arg,
+					sizeof(struct serial_struct));			
+			if (error)
+				return error;
+			return isicom_set_serial_info(port,
+					(struct serial_struct *) arg);
+					
+		default:
+			return -ENOIOCTLCMD;						
+	}
+	return 0;
+}
+
+/* set_termios et all */
+static void isicom_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	unsigned long flags;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_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();
+	isicom_config_port(port);
+	restore_flags(flags);
+	
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {	
+		tty->hw_stopped = 0;
+		isicom_start(tty);   
+	}    
+}
+
+/* throttle et all */
+static void isicom_throttle(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	struct isi_board * card = port->card;
+	unsigned long flags;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_throttle"))
+		return;
+	
+	/* tell the card that this port cannot handle any more data for now */
+	save_flags(flags); cli();
+	card->port_status &= ~(1 << port->channel);
+	outw(card->port_status, card->base + 0x02);
+	restore_flags(flags);
+}
+
+/* unthrottle et all */
+static void isicom_unthrottle(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	struct isi_board * card = port->card;
+	unsigned long flags;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_unthrottle"))
+		return;
+	
+	/* tell the card that this port is ready to accept more data */
+	save_flags(flags); cli();
+	card->port_status |= (1 << port->channel);
+	outw(card->port_status, card->base + 0x02);
+	restore_flags(flags);
+}
+
+/* stop et all */
+static void isicom_stop(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+
+	if (isicom_paranoia_check(port, tty->device, "isicom_stop"))
+		return;
+	
+	/* this tells the transmitter not to consider this port for
+	   data output to the card. */
+	port->status &= ~ISI_TXOK;
+}
+
+/* start et all */
+static void isicom_start(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_start"))
+		return;
+	
+	/* this tells the transmitter to consider this port for
+	   data output to the card. */
+	port->status |= ISI_TXOK;
+}
+
+/* hangup et all */
+static void do_isicom_hangup(void * data)
+{
+	struct isi_port * port = (struct isi_port *) data;
+	struct tty_struct * tty;
+	
+	tty = port->tty;
+	if (!tty)
+		return;
+		
+	tty_hangup(tty);	
+}
+
+static void isicom_hangup(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_hangup"))
+		return;
+	
+	isicom_shutdown_port(port);
+	port->count = 0;
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+	port->tty = 0;
+	wake_up_interruptible(&port->open_wait);
+}
+
+/* flush_buffer et all */
+static void isicom_flush_buffer(struct tty_struct * tty)
+{
+	struct isi_port * port = (struct isi_port *) tty->driver_data;
+	unsigned long flags;
+	
+	if (isicom_paranoia_check(port, tty->device, "isicom_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 register_ioregion(void)
+{
+	int count, done=0;
+	for (count=0; count < BOARD_COUNT; count++ ) {
+		if (isi_card[count].base) {
+			if (check_region(isi_card[count].base,16)) {
+				printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x is busy. Card%d will be disabled.\n",
+					isi_card[count].base,isi_card[count].base+15,count+1);
+				isi_card[count].base=0;
+			}
+			else {
+				request_region(isi_card[count].base,16,ISICOM_NAME);
+#ifdef ISICOM_DEBUG				
+				printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x requested for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1);
+#endif				
+				done++;
+			}
+		}	
+	}
+	return done;
+}
+
+static void unregister_ioregion(void)
+{
+	int count;
+	for (count=0; count < BOARD_COUNT; count++ ) 
+		if (isi_card[count].base) {
+			release_region(isi_card[count].base,16);
+#ifdef ISICOM_DEBUG			
+			printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x released for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1);
+#endif			
+		}
+}
+
+static int register_drivers(void)
+{
+	int error;
+
+	/* tty driver structure initialization */
+	memset(&isicom_normal, 0, sizeof(struct tty_driver));
+	isicom_normal.magic	= TTY_DRIVER_MAGIC;
+	isicom_normal.name 	= "ttyM";
+	isicom_normal.major	= ISICOM_NMAJOR;
+	isicom_normal.minor_start	= 0;
+	isicom_normal.num	= PORT_COUNT;
+	isicom_normal.type	= TTY_DRIVER_TYPE_SERIAL;
+	isicom_normal.subtype	= SERIAL_TYPE_NORMAL;
+	isicom_normal.init_termios	= tty_std_termios;
+	isicom_normal.init_termios.c_cflag	= 
+				B9600 | CS8 | CREAD | HUPCL |CLOCAL;
+	isicom_normal.flags	= TTY_DRIVER_REAL_RAW;
+	isicom_normal.refcount	= &isicom_refcount;
+	
+	isicom_normal.table	= isicom_table;
+	isicom_normal.termios	= isicom_termios;
+	isicom_normal.termios_locked	= isicom_termios_locked;
+	
+	isicom_normal.open	= isicom_open;
+	isicom_normal.close	= isicom_close;
+	isicom_normal.write	= isicom_write;
+	isicom_normal.put_char	= isicom_put_char;
+	isicom_normal.flush_chars	= isicom_flush_chars;
+	isicom_normal.write_room	= isicom_write_room;
+	isicom_normal.chars_in_buffer	= isicom_chars_in_buffer;
+	isicom_normal.ioctl	= isicom_ioctl;
+	isicom_normal.set_termios	= isicom_set_termios;
+	isicom_normal.throttle	= isicom_throttle;
+	isicom_normal.unthrottle	= isicom_unthrottle;
+	isicom_normal.stop	= isicom_stop;
+	isicom_normal.start	= isicom_start;
+	isicom_normal.hangup	= isicom_hangup;
+	isicom_normal.flush_buffer	= isicom_flush_buffer;
+	
+	/*	callout device	*/
+	
+	isicom_callout	= isicom_normal;
+	isicom_callout.name	= "cum"; 
+	isicom_callout.major	= ISICOM_CMAJOR;
+	isicom_callout.subtype	= SERIAL_TYPE_CALLOUT;
+	
+	if ((error=tty_register_driver(&isicom_normal))!=0) {
+		printk(KERN_DEBUG "ISICOM: Couldn't register the dialin driver, error=%d\n",
+			error);
+		return error;
+	}
+	if ((error=tty_register_driver(&isicom_callout))!=0) {
+		tty_unregister_driver(&isicom_normal);
+		printk(KERN_DEBUG "ISICOM: Couldn't register the callout driver, error=%d\n",
+			error);
+		return error;	
+	}
+	return 0;
+}
+
+static void unregister_drivers(void)
+{
+	int error;
+	if ((error=tty_unregister_driver(&isicom_callout))!=0)
+		printk(KERN_DEBUG "ISICOM: couldn't unregister callout driver error=%d.\n",error);
+	if (tty_unregister_driver(&isicom_normal))
+		printk(KERN_DEBUG "ISICOM: couldn't unregister normal driver error=%d.\n",error);
+}
+
+static int register_isr(void)
+{
+	int count, done=0;
+	for (count=0; count < BOARD_COUNT; count++ ) {
+		if (isi_card[count].base) {
+			if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT, ISICOM_NAME, NULL)) {
+				printk(KERN_WARNING "ISICOM: Could not install handler at Irq %d. Card%d will be disabled.\n",
+					isi_card[count].irq, count+1);
+				release_region(isi_card[count].base,16);
+				isi_card[count].base=0;
+			}
+			else {
+				printk(KERN_INFO "ISICOM: Card%d at 0x%x using irq %d.\n", 
+				count+1, isi_card[count].base, isi_card[count].irq); 
+				
+				irq_to_board[isi_card[count].irq]=&isi_card[count];
+				done++;
+			}
+		}	
+	}
+	return done;
+}
+
+static void unregister_isr(void)
+{
+	int count;
+	for (count=0; count < BOARD_COUNT; count++ ) 
+		if (isi_card[count].base) {
+			free_irq(isi_card[count].irq, NULL);
+#ifdef ISICOM_DEBUG			
+			printk(KERN_DEBUG "ISICOM: Irq %d released for Card%d.\n",isi_card[count].irq, count+1);
+#endif			
+		}
+}
+
+static int isicom_init(void)
+{
+	int card, channel, base;
+	struct isi_port * port;
+	unsigned long page;
+	
+	if (!tmp_buf) { 
+		page = get_free_page(GFP_KERNEL);
+	      	if (!page) {
+#ifdef ISICOM_DEBUG	      	
+	      		printk(KERN_DEBUG "ISICOM: Couldn't allocate page for tmp_buf.\n");
+#else
+			printk(KERN_ERR "ISICOM: Not enough memory...\n");
+#endif	      
+	      		return 0;
+	      	}	
+	      	tmp_buf = (unsigned char *) page;
+	}
+	
+	if (!register_ioregion()) 
+	{
+		printk(KERN_ERR "ISICOM: All required I/O space found busy.\n");
+		free_page((unsigned long)tmp_buf);
+		return 0;
+	}
+	if (register_drivers()) 
+	{
+		unregister_ioregion();
+		free_page((unsigned long)tmp_buf);
+		return 0;
+	}
+	if (!register_isr()) 
+	{
+		unregister_drivers();
+		/*  ioports already uregistered in register_isr */
+		free_page((unsigned long)tmp_buf);
+		return 0;		
+	}
+	
+	/* initialize bottom half  */
+	init_bh(ISICOM_BH, do_isicom_bh);
+
+
+	memset(isi_ports, 0, sizeof(isi_ports));
+	for (card = 0; card < BOARD_COUNT; card++) {
+		port = &isi_ports[card * 16];
+		isi_card[card].ports = port;
+		base = isi_card[card].base;
+		for (channel = 0; channel < 16; channel++, port++) {
+			port->magic = ISICOM_MAGIC;
+			port->card = &isi_card[card];
+			port->channel = channel;		
+			port->normal_termios = isicom_normal.init_termios;
+			port->callout_termios = isicom_callout.init_termios;
+		 	port->close_delay = 50 * HZ/100;
+		 	port->closing_wait = 3000 * HZ/100;
+			port->hangup_tq.routine = do_isicom_hangup;
+		 	port->hangup_tq.data = port;
+		 	port->bh_tqueue.routine = isicom_bottomhalf;
+		 	port->bh_tqueue.data = port;
+		 	port->status = 0;
+		 				
+			/*  . . .  */
+ 		}
+	} 
+	
+	return 1;	
+}
+
+/*
+ *	Insmod can set static symbols so keep these static
+ */
+ 
+static int ISIBase1=0, ISIBase2=0, ISIBase3=0, ISIBase4=0; 
+static int Irq1=0, Irq2=0, Irq3=0, Irq4=0;
+
+int init_module(void)
+{
+	int retval, card;
+	
+	isi_card[0].base=ISIBase1;
+	isi_card[1].base=ISIBase2;
+	isi_card[2].base=ISIBase3;
+	isi_card[3].base=ISIBase4;
+			
+	isi_card[0].irq=Irq1;
+	isi_card[1].irq=Irq2;
+	isi_card[2].irq=Irq3;
+	isi_card[3].irq=Irq4;
+	
+	for (card=0 ;card < BOARD_COUNT; card++) {
+		if (!((isi_card[card].irq==2)||(isi_card[card].irq==3)||
+		    (isi_card[card].irq==4)||(isi_card[card].irq==5)||
+		    (isi_card[card].irq==7)||(isi_card[card].irq==10)||
+		    (isi_card[card].irq==11)||(isi_card[card].irq==12)||
+		    (isi_card[card].irq==15))) {
+			
+			if (isi_card[card].base) {
+				printk(KERN_ERR "ISICOM: Irq %d unsupported. Disabling Card%d...\n",
+					isi_card[card].irq, card+1);
+				isi_card[card].base=0;
+			}	
+		}
+	}	
+	
+	if (!(isi_card[0].base || isi_card[1].base || isi_card[2].base || isi_card[3].base)) {
+		printk(KERN_ERR "ISICOM: No valid card configuration. Driver cannot be initialized...\n"); 
+		return -EIO;
+	}		
+	retval=misc_register(&isiloader_device);
+	if (retval<0) {
+		printk(KERN_ERR "ISICOM: Unable to register firmware loader driver.\n");
+		return -EIO;
+	}
+	
+	if (!isicom_init()) {
+		if (misc_deregister(&isiloader_device)) 
+			printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n");
+		return -EIO;
+	}
+	
+	init_timer(&tx);
+	tx.expires = jiffies + 1;
+	tx.data = 0;
+	tx.function = isicom_tx;
+	re_schedule = 1;
+	add_timer(&tx);
+	
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	re_schedule = 0;
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + HZ;
+	schedule();
+	disable_bh(ISICOM_BH);
+	
+#ifdef ISICOM_DEBUG	
+	printk("ISICOM: isicom_tx tx_count = %ld.\n", tx_count);
+#endif	
+
+#ifdef ISICOM_DEBUG
+	printk("ISICOM: uregistering isr ...\n");
+#endif	
+	unregister_isr();
+
+#ifdef ISICOM_DEBUG	
+	printk("ISICOM: unregistering drivers ...\n");
+#endif
+	unregister_drivers();
+	
+#ifdef ISICOM_DEBUG	
+	printk("ISICOM: unregistering ioregion ...\n");
+#endif	
+	unregister_ioregion();	
+	
+#ifdef ISICOM_DEBUG	
+	printk("ISICOM: freeing tmp_buf ...\n");
+#endif	
+	free_page((unsigned long)tmp_buf);
+	
+#ifdef ISICOM_DEBUG		
+	printk("ISICOM: unregistering firmware loader ...\n");	
+#endif
+	if (misc_deregister(&isiloader_device))
+		printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n");
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.0.35/linux/drivers/char/lp.c	Sun Nov 15 10:49:34 1998
+++ linux/drivers/char/lp.c	Sun Nov 15 10:32:54 1998
@@ -89,14 +89,18 @@
 	while(wait != LP_WAIT(minor)) wait++;
 	/* control port takes strobe high */
 	outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
-	/* Wait until NBUSY line goes high */
-	count = 0;
-	do {
-		status = LP_S(minor);
-		count++;
-		if (need_resched)
-			schedule();
-	} while (LP_READY(minor, status) && (count<LP_CHAR(minor)));
+	
+	if(LP_F(minor)&LP_STRICT)
+	{
+		/* Wait until NBUSY line goes high */
+		count = 0;
+		do {
+			status = LP_S(minor);
+			count++;
+		} while (LP_READY(minor, status) && (count<LP_CHAR(minor)));
+	}
+	else while(wait) wait--;
+	
 	/* take strobe low */
 	outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
 	/* update waittime statistics */
@@ -435,6 +439,12 @@
 				LP_F(minor) |= LP_CAREFUL;
 			else
 				LP_F(minor) &= ~LP_CAREFUL;
+			break;
+		case LPSTRICT:
+			if (arg)
+				LP_F(minor) |= LP_STRICT;
+			else
+				LP_F(minor) &= ~LP_STRICT;
 			break;
 		case LPWAIT:
 			LP_WAIT(minor) = arg;
diff -u --recursive --new-file v2.0.35/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.0.35/linux/drivers/char/mem.c	Mon Jul 13 13:46:28 1998
+++ linux/drivers/char/mem.c	Sun Nov 15 10:32:54 1998
@@ -400,7 +400,7 @@
 #if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \
     defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
     defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \
-    defined (CONFIG_PCWATCHDOG) || defined (CONFIG_H8) || \
+    defined (CONFIG_PCWATCHDOG) || defined (CONFIG_H8) || defined(CONFIG_WDT) || \
     defined (CONFIG_APM) || defined (CONFIG_RTC) || defined (CONFIG_SUN_MOUSE)
 	misc_init();
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.0.35/linux/drivers/char/random.c	Mon Jul 13 13:46:28 1998
+++ linux/drivers/char/random.c	Sun Nov 15 10:32:54 1998
@@ -1500,7 +1500,7 @@
 {
 	unsigned long low, high;
 	__asm__(".byte 0x0f,0x31" :"=a" (low), "=d" (high));
-	return (((unsigned long long) high << 31) | low); 
+	return (((unsigned long long) high << 32) | low); 
 }
 
 static void initialize_benchmark(struct random_benchmark *bench,
diff -u --recursive --new-file v2.0.35/linux/drivers/char/specialix.c linux/drivers/char/specialix.c
--- v2.0.35/linux/drivers/char/specialix.c	Mon Jul 13 13:46:28 1998
+++ linux/drivers/char/specialix.c	Sun Nov 15 10:32:55 1998
@@ -56,10 +56,14 @@
  *                Changed the specialix=... format to include interrupt.
  * Revision 1.7:  May 27 1997
  *                Made many more debug printk's a compile time option.
+ * Revision 1.8:  Port to 2.1 kernel. -- Not here.
+ *
+ * Revision 1.9:  October 1 1998
+ *                Support for the PCI version of the card.
  * 
  */
 
-#define VERSION "1.7"
+#define VERSION "1.9"
 
 
 /*
@@ -84,7 +88,8 @@
 #include <linux/delay.h>
 #include <linux/tqueue.h>
 #include <linux/version.h>
-
+#include <linux/pci.h>
+#include <linux/bios32.h>
 
 /* ************************************************************** */
 /* * This section can be removed when 2.0 becomes outdated....  * */
@@ -170,7 +175,6 @@
 #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, };
@@ -322,23 +326,28 @@
 
 extern inline int sx_check_io_range(struct specialix_board * bp)
 {
-	return check_region (bp->base, SX_IO_SPACE);
+	return check_region (bp->base, 
+	               bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
 }
 
 
 extern inline void sx_request_io_range(struct specialix_board * bp)
 {
-	request_region(bp->base, SX_IO_SPACE, "specialix IO8+" );
+	request_region(bp->base,
+	               bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE, 
+	               "specialix IO8+" );
 }
 
 
 extern inline void sx_release_io_range(struct specialix_board * bp)
 {
-	release_region(bp->base, SX_IO_SPACE);
+	release_region(bp->base,
+	               bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
 }
 
 	
 /* Must be called with enabled interrupts */
+/* Ugly. Don't use this for anything else than initialization code */
 extern inline void sx_long_delay(unsigned long delay)
 {
 	unsigned long i;
@@ -354,6 +363,8 @@
 	int virq;
 	int i;
 
+	if (bp->flags & SX_BOARD_IS_PCI) 
+		return 1;
 	switch (bp->irq) {
 	/* In the same order as in the docs... */
 	case 15: virq = 0;break;
@@ -481,9 +492,14 @@
 	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);
+	/* They managed to switch the bit order between the docs and
+	   the IO8+ card. The new PCI card now conforms to old docs.
+	   They changed the PCI docs to reflect the situation on the
+	   old card. */
+	val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
+	if (val1 != val2) {
+		printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
+		       board_No(bp), val2, bp->base, val1);
 		return 1;
 	}
 
@@ -865,7 +881,7 @@
 	unsigned long loop = 0;
 	int saved_reg;
 
-	bp = IRQ_to_board[irq];
+	bp = dev_id;
 	
 	if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
 #ifdef SPECIALIX_DEBUG 
@@ -921,6 +937,25 @@
  *  Routines for open & close processing.
  */
 
+void turn_ints_off (struct specialix_board *bp)
+{
+	if (bp->flags & SX_BOARD_IS_PCI) {
+		/* This was intended for enabeling the interrupt on the
+		 * PCI card. However it seems that it's already enabled
+		 * and as PCI interrupts can be shared, there is no real
+		 * reason to have to turn it off. */
+	}
+	(void) sx_in_off (bp, 0); /* Turn off interrupts. */
+}
+
+void turn_ints_on (struct specialix_board *bp)
+{
+	if (bp->flags & SX_BOARD_IS_PCI) {
+		/* play with the PCI chip. See comment above */
+	}
+	(void) sx_in (bp, 0); /* Turn ON interrupts. */
+}
+
 
 /* Called with disabled interrupts */
 extern inline int sx_setup_board(struct specialix_board * bp)
@@ -930,14 +965,12 @@
 	if (bp->flags & SX_BOARD_ACTIVE) 
 		return 0;
 
-	error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL);
+	error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp);
 
 	if (error) 
 		return error;
 
-	IRQ_to_board[bp->irq] = bp;
-	(void) sx_in (bp, 0); /* Turn ON interrupts. */
-
+	turn_ints_on (bp);
 	bp->flags |= SX_BOARD_ACTIVE;
 
 	MOD_INC_USE_COUNT;
@@ -953,11 +986,13 @@
 	
 	bp->flags &= ~SX_BOARD_ACTIVE;
 	
-	free_irq(bp->irq, NULL);
-	(void) sx_in_off (bp, 0); /* Turn off interrupts. */
+#if SPECIALIX_DEBUG > 2
+	printk ("Freeing IRQ%d for board %d.\n", bp->irq, board_No (bp));
+#endif
+	free_irq(bp->irq, bp);
+
+	turn_ints_off (bp);
 
-	IRQ_to_board[bp->irq] = NULL;
-	
 	MOD_DEC_USE_COUNT;
 }
 
@@ -1042,12 +1077,14 @@
 		/* 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",
+			        "Performance degradation is possible.\n"
+			        "Read specialix.txt for more info.\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", 
+			        "This might not work.\n"
+			        "Read specialix.txt for more info.\n", 
 			        port_No (port), tmp);
 		}
 	}
@@ -2150,7 +2187,6 @@
 		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";
@@ -2240,7 +2276,7 @@
 void specialix_setup(char *str, int * ints)
 {
 	int i;
-        
+	
 	for (i=0;i<SX_NBOARD;i++) {
 		sx_board[i].base = 0;
 	}
@@ -2261,8 +2297,12 @@
 {
 	int i;
 	int found = 0;
+#ifdef CONFIG_PCI
+	int pci_index;
+	unsigned char pci_bus, pci_device_fn;
+#endif
 
-	printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n");
+	printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\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");
@@ -2277,6 +2317,36 @@
 		if (sx_board[i].base && !sx_probe(&sx_board[i]))
 			found++;
 
+#ifdef CONFIG_PCI
+	i=0;
+	pci_index = 0;
+	while ((i < SX_NBOARD) && (pci_index < 0xff)) {
+		unsigned char tbyte;
+		unsigned int tint;
+		if (sx_board[i].flags & SX_BOARD_PRESENT) {
+			i++;
+			continue;
+		}
+		if (pcibios_find_device (PCI_VENDOR_ID_SPECIALIX, 
+		                         PCI_DEVICE_ID_SPECIALIX_IO8,
+		                         pci_index, &pci_bus, &pci_device_fn)
+		    != PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+		                         PCI_INTERRUPT_LINE, &tbyte);
+		sx_board[i].irq = tbyte;
+
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+		                          PCI_BASE_ADDRESS_2, &tint);
+		/* Mask out the fact that it's IO-space */
+		sx_board[i].base = tint & PCI_BASE_ADDRESS_IO_MASK; 
+		sx_board[i].flags |= SX_BOARD_IS_PCI;
+		if (!sx_probe(&sx_board[i]))
+			found ++;
+		pci_index++;
+	}
+#endif
+
 	if (!found) {
 		sx_release_drivers();
 		printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
@@ -2293,7 +2363,8 @@
 
 /*
  * You can setup up to 4 boards.
- * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
+ * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
+ * You should specify the IRQs too in that case "irq=....,...". 
  * 
  * More than 4 boards in one computer is not possible, as the card can
  * only use 4 different interrupts. 
diff -u --recursive --new-file v2.0.35/linux/drivers/char/specialix_io8.h linux/drivers/char/specialix_io8.h
--- v2.0.35/linux/drivers/char/specialix_io8.h	Mon Jul 13 13:46:28 1998
+++ linux/drivers/char/specialix_io8.h	Sun Nov 15 10:32:55 1998
@@ -43,15 +43,21 @@
 
 #ifdef __KERNEL__
 
-#define SX_NBOARD		4
+/* You can have max 4 ISA cards in one PC, and I recommend not much 
+more than a few  PCI versions of the card. */
+
+#define SX_NBOARD		8
+
 /* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
 #define SX_IO_SPACE             4
+/* The PCI version decodes 8 addresses, but still only 2 are used. */
+#define SX_PCI_IO_SPACE         8
+
 /* 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. */
 
@@ -93,6 +99,7 @@
 
 #define SX_BOARD_PRESENT	0x00000001
 #define SX_BOARD_ACTIVE		0x00000002
+#define SX_BOARD_IS_PCI		0x00000004
 
 
 struct specialix_port {
diff -u --recursive --new-file v2.0.35/linux/drivers/char/tga.c linux/drivers/char/tga.c
--- v2.0.35/linux/drivers/char/tga.c	Mon Jul 13 13:46:28 1998
+++ linux/drivers/char/tga.c	Sun Nov 15 10:32:55 1998
@@ -10,6 +10,7 @@
  * This module exports the console io support for DEC's TGA
  */
 
+#include <linux/config.h> /* CONFIG_VGA_CONSOLE */
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/interrupt.h>
diff -u --recursive --new-file v2.0.35/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.0.35/linux/drivers/char/tty_io.c	Mon Jul 13 13:46:28 1998
+++ linux/drivers/char/tty_io.c	Sun Nov 15 10:32:55 1998
@@ -2049,9 +2049,6 @@
 #ifdef CONFIG_RISCOM8
 	riscom8_init();
 #endif
-#ifdef CONFIG_BAYCOM
-	baycom_init();
-#endif
 #ifdef CONFIG_SPECIALIX
 	specialix_init();
 #endif
@@ -2059,4 +2056,3 @@
 	vcs_init();
 	return 0;
 }
-
diff -u --recursive --new-file v2.0.35/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.0.35/linux/drivers/char/vt.c	Sun Nov 15 10:49:34 1998
+++ linux/drivers/char/vt.c	Sun Nov 15 10:32:56 1998
@@ -172,8 +172,10 @@
 	if (hz > 20 && hz < 32767)
 		count = 1193180 / hz;
         
+        if (!count)
+        	kd_nosound(0);
         /* ignore multiple simultaneous requests for sound */
-        if (!set_bit(0, &mksound_lock)) {
+        else if (!set_bit(0, &mksound_lock)) {
         /* set_bit in 2.0.x is same as test-and-set in 2.1.x */
                 del_timer(&sound_timer);
                 if (count) {
@@ -189,9 +191,7 @@
                                 sound_timer.expires = jiffies+ticks;
                                 add_timer(&sound_timer);
                         }
-                } else
-                        kd_nosound(0);
- 
+		} 
                 mksound_lock = 0;
         }	
 	return;
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.0.35/linux/drivers/isdn/Config.in	Mon Aug  4 17:33:59 1997
+++ linux/drivers/isdn/Config.in	Sun Nov 15 10:32:56 1998
@@ -7,20 +7,43 @@
     bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
     bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
   fi
+  #bool 'Support dynamic timeout-rules' CONFIG_ISDN_TIMEOUT_RULES
+  #if [ "$CONFIG_ISDN_TIMEOUT_RULES" != "n" ]; then
+  #  bool 'Use masqueraded addresses for rule-matching' CONFIG_ISDN_TIMRU_USE_MASQ
+  #fi
+  #bool 'Support budget-accounting' CONFIG_ISDN_BUDGET
 fi
 bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO
 dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
+dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
 dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $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
+    if [ "$CONFIG_HISAX_EURO" != "n" ]; then
+	    bool 'Support for german chargeinfo' CONFIG_DE_AOC
+	    bool 'Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
+	    bool 'Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
+    fi
     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 Teles 16.3c' CONFIG_HISAX_TELES3C 
+    bool 'HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI 
+    bool 'HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX 
     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 AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
+    bool 'HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
+    bool 'HiSax Support for Elsa cards' CONFIG_HISAX_ELSA
     bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
+    bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
+    bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM
+    bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT
+    bool 'HiSax Support for Sedlbauer cards' CONFIG_HISAX_SEDLBAUER
+    bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
+    bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC
+    bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET
+    bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
 fi
 if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
 	dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/b1capi.c linux/drivers/isdn/avmb1/b1capi.c
--- v2.0.35/linux/drivers/isdn/avmb1/b1capi.c	Thu Aug 21 15:14:34 1997
+++ linux/drivers/isdn/avmb1/b1capi.c	Sun Nov 15 10:32:56 1998
@@ -1,11 +1,74 @@
 /*
- * $Id: b1capi.c,v 1.4.2.1 1997/07/12 08:18:59 calle Exp $
+ * $Id: b1capi.c,v 1.4.2.19 1998/10/25 14:36:14 fritz 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.19  1998/10/25 14:36:14  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.4.2.18  1998/03/20 20:34:37  calle
+ * port valid check now only for T1, because of the PCI and PCMCIA cards.
+ *
+ * Revision 1.4.2.17  1998/03/20 14:38:17  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.4.2.16  1998/03/20 09:01:08  calle
+ * Changes capi_register handling to get full support for 30 bchannels.
+ *
+ * Revision 1.4.2.15  1998/03/18 17:43:26  calle
+ * T1 with fastlink, bugfix for multicontroller support in capidrv.c
+ *
+ * Revision 1.4.2.14  1998/03/04 17:33:47  calle
+ * Changes for T1.
+ *
+ * Revision 1.4.2.13  1998/02/27 15:40:41  calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
+ * Revision 1.4.2.12  1998/02/24 17:58:25  calle
+ * changes for T1.
+ *
+ * Revision 1.4.2.11  1998/01/27 16:12:49  calle
+ * Support for PCMCIA B1/M1/M2 ready.
+ *
+ * Revision 1.4.2.10  1998/01/26 14:53:30  calle
+ * interface change for pcmcia cards
+ *
+ * Revision 1.4.2.9  1998/01/23 16:49:27  calle
+ * added functions for pcmcia cards,
+ * avmb1_addcard returns now the controller number.
+ *
+ * Revision 1.4.2.8  1998/01/16 14:04:15  calle
+ * Decoding of manufacturer part of capi_profile, now show linetype and
+ * protocol if possible.
+ *
+ * Revision 1.4.2.7  1998/01/15 15:33:34  calle
+ * print cardtype, d2 protocol and linetype after load.
+ *
+ * Revision 1.4.2.6  1997/12/08 06:58:41  calle
+ * correct typo.
+ *
+ * Revision 1.4.2.5  1997/12/07 19:59:54  calle
+ * more changes for M1/T1/B1 + config
+ *
+ * Revision 1.4.2.4  1997/11/26 16:57:20  calle
+ * more changes for B1/M1/T1.
+ *
+ * Revision 1.4.2.3  1997/11/26 10:46:52  calle
+ * prepared for M1 (Mobile) and T1 (PMX) cards.
+ * prepared to set configuration after load to support other D-channel
+ * protocols, point-to-point and leased lines.
+ *
+ * Revision 1.4.2.2  1997/10/19 14:44:36  calle
+ * fixed capi_get_version.
+ *
  * 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.
@@ -52,20 +115,16 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
-static char *revision = "$Revision: 1.4.2.1 $";
+static char *revision = "$Revision: 1.4.2.19 $";
 
 /* ------------------------------------------------------------- */
 
-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(showcapimsgs, "0-5i");
 MODULE_PARM(loaddebug, "0-1i");
 #endif
 
@@ -99,7 +158,7 @@
 
 /* ------------------------------------------------------------- */
 
-static struct capi_version driver_version = {2, 0, 0, 9};
+static struct capi_version driver_version = {2, 0, 1, 1<<4};
 static char driver_serial[CAPI_SERIAL_LEN] = "4711";
 static char capi_manufakturer[64] = "AVM Berlin";
 
@@ -111,7 +170,7 @@
 
 #define NCCI2CTRL(ncci)    (((ncci) >> 24) & 0x7f)
 
-#define VALID_CARD(c)	   ((c) > 0 && (c) <= ncards)
+#define VALID_CARD(c)	   ((c) > 0 && (c) <= CAPI_MAXCONTR)
 #define CARD(c)		   (&cards[(c)-1])
 #define CARDNR(cp)	   (((cp)-cards)+1)
 
@@ -128,6 +187,17 @@
 
 /* -------- util functions ------------------------------------ */
 
+static char *cardtype2str(int cardtype)
+{
+	switch (cardtype) {
+		default:
+		case AVM_CARDTYPE_B1: return "B1";
+		case AVM_CARDTYPE_M1: return "M1";
+		case AVM_CARDTYPE_M2: return "M2";
+		case AVM_CARDTYPE_T1: return "T1";
+	}
+}
+
 static inline int capi_cmd_valid(__u8 cmd)
 {
 	switch (cmd) {
@@ -271,7 +341,7 @@
 			}
 		}
 		APPL(appl)->releasing--;
-		if (APPL(appl)->releasing == 0) {
+		if (APPL(appl)->releasing <= 0) {
 	                APPL(appl)->signal = 0;
 			APPL_MARK_FREE(appl);
 			printk(KERN_INFO "b1capi: appl %d down\n", appl);
@@ -404,25 +474,36 @@
 
 /* -------- card ready callback ------------------------------- */
 
+
 void avmb1_card_ready(avmb1_card * card)
 {
+        struct capi_profile *profp =
+			(struct capi_profile *)card->version[VER_PROFILE];
+	char *dversion = card->version[VER_DRIVER];
 	__u16 appl;
+	char *cardname, cname[20];
+	__u32 flag;
+        int nbchan = profp->nbchannel;
 
 	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->cversion.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
+	card->cversion.majormanuversion |= ((dversion[2] - '0') & 0xf);
+	card->cversion.minormanuversion = (dversion[3] - '0') << 4;
+	card->cversion.minormanuversion |=
+		(dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
 	card->cardstate = CARD_RUNNING;
 
-
 	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
 		if (VALID_APPLID(appl) && !APPL(appl)->releasing) {
+			int nconn, want = APPL(appl)->rparam.level3cnt;
+
+			if (want > 0) nconn = want;
+			else nconn = nbchan * -want;
+			if (nconn == 0) nconn = nbchan;
+
 			B1_send_register(card->port, appl,
-				1024 * (APPL(appl)->rparam.level3cnt+1),
-				APPL(appl)->rparam.level3cnt,
+				1024 * (nconn+1), nconn,
 				APPL(appl)->rparam.datablkcnt,
 				APPL(appl)->rparam.datablklen);
 		}
@@ -430,10 +511,45 @@
 
         set_bit(CARDNR(card), &notify_up_set);
         queue_task(&tq_state_notify, &tq_scheduler);
-        printk(KERN_NOTICE "b1capi: card %d ready.\n", CARDNR(card));
+
+        flag = ((__u8 *)(profp->manu))[1];
+        switch (flag) {
+	case 0: cardname = cardtype2str(card->cardtype); break;
+	case 3: cardname = "PCMCIA B"; break;
+	case 4: cardname = "PCMCIA M1"; break;
+	case 5: cardname = "PCMCIA M2"; break;
+	case 6: cardname = "B1 V3.0"; break;
+	case 7: cardname = "B1 PCI"; break;
+	default: cardname = cname; break;
+                 sprintf(cname, "AVM?%u", (unsigned int)flag);
+                 break;
+        }
+        printk(KERN_NOTICE "b1capi: card %d \"%s\" ready.\n",
+		CARDNR(card), cardname);
+        flag = ((__u8 *)(profp->manu))[3];
+        if (flag)
+		printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n",
+			CARDNR(card),
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+        flag = ((__u8 *)(profp->manu))[5];
+	if (flag)
+		printk(KERN_NOTICE "b1capi: card %d Linetype:%s%s%s%s\n",
+			CARDNR(card),
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
 }
 
-static void avmb1_card_down(avmb1_card * card)
+static void avmb1_card_down(avmb1_card * card, int notify)
 {
 	__u16 appl;
 
@@ -460,52 +576,135 @@
 
 /* ------------------------------------------------------------- */
 
-int avmb1_addcard(int port, int irq)
+
+int avmb1_registercard(int port, int irq, int cardtype, int allocio)
 {
 	struct avmb1_card *card;
-	int irqval;
+	int irqval,i;
 
 
-	card = &cards[ncards];
+	for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ;
+   
+	if (i == CAPI_MAXCONTR) {
+		printk(KERN_ERR "b1capi: out of controller slots\n");
+	   	return -ENFILE;
+	}
+
+	card = &cards[i];
 	memset(card, 0, sizeof(avmb1_card));
-	sprintf(card->name, "avmb1-%d", ncards + 1);
+	sprintf(card->name, "avmb1-%d", CARDNR(card));
 
-	request_region(port, AVMB1_PORTLEN, card->name);
+        if (allocio)
+		request_region(port, AVMB1_PORTLEN, card->name);
 
-	if ((irqval = request_irq(irq, avmb1_interrupt, SA_SHIRQ, card->name, card)) != 0) {
+	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;
+		release_region(port, AVMB1_PORTLEN);
+		return -EBUSY;
 	}
+
+	card->cardstate = CARD_DETECTED;
 	ncards++;
-	card->cnr = ncards;
+	card->cnr = CARDNR(card);
 	card->port = port;
 	card->irq = irq;
-	card->cardstate = CARD_DETECTED;
-	return 0;
+	card->cardtype = cardtype;
+	return card->cnr;
+}
+
+int avmb1_addcard(int port, int irq, int cardtype)
+{
+	return avmb1_registercard(port, irq, cardtype, 1);
 }
 
-int avmb1_probecard(int port, int irq)
+int avmb1_detectcard(int port, int irq, int cardtype)
 {
 	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 (!B1_valid_irq(irq, cardtype)) {
+		printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n",
+				irq, cardtype2str(cardtype));
+		return -EINVAL;
+	}
+	if (!B1_valid_port(port, cardtype)) {
+		printk(KERN_WARNING "b1capi: port 0x%x not valid for %s-card.\n",
+				port, cardtype2str(cardtype));
+		return -EINVAL;
 	}
-	if ((rc = B1_detect(port)) != 0) {
-		printk(KERN_NOTICE "b1capi: NO card at 0x%x (%d)\n", port, rc);
+	B1_reset(port);
+	if ((rc = B1_detect(port, cardtype)) != 0) {
+		printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n",
+					  cardtype2str(cardtype), port, rc);
 		return -EIO;
 	}
 	B1_reset(port);
-	printk(KERN_NOTICE "b1capi: AVM-B1-Controller detected at 0x%x\n", port);
+	switch (cardtype) {
+		default:
+	   	case AVM_CARDTYPE_M1:
+	   	case AVM_CARDTYPE_M2:
+	   	case AVM_CARDTYPE_B1:
+	    		printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port);
+			break;
+	   	case AVM_CARDTYPE_T1:
+			break;
+	}
+
+	return 0;
+}
+
+int avmb1_probecard(int port, int irq, int cardtype)
+{
+	if (check_region(port, AVMB1_PORTLEN)) {
+		printk(KERN_WARNING
+		       "b1capi: ports 0x%03x-0x%03x in use.\n",
+		       port, port + AVMB1_PORTLEN);
+		return -EBUSY;
+	}
+        return avmb1_detectcard(port, irq, cardtype);
+}
+
+int avmb1_unregistercard(int cnr, int freeio)
+{
+	avmb1_card * card;
+   	if (!VALID_CARD(cnr)) 
+		return -ESRCH;
+	card = CARD(cnr);
+
+	if (card->cardstate == CARD_FREE)
+		return -ESRCH;
+	if (card->cardstate == CARD_RUNNING)
+		avmb1_card_down(card, freeio);
+
+	if (card->cardstate != CARD_FREE)
+		if (card->cardtype == AVM_CARDTYPE_T1)
+			T1_reset(card->port);
+
+	free_irq(card->irq, card);
+	if (freeio)
+		release_region(card->port, AVMB1_PORTLEN);
+	card->cardstate = CARD_FREE;
+	return 0;
+}
+
+int avmb1_resetcard(int cnr)
+{
+	avmb1_card * card;
+
+	if (!VALID_CARD(cnr))
+		return -ESRCH;
+	card = CARD(cnr);
+	if (card->cardstate == CARD_FREE)
+		return -ESRCH;
+
+	if (card->cardstate == CARD_RUNNING)
+		avmb1_card_down(card, 0);
+
+	B1_reset(card->port);
+	B1_reset(card->port);
+
+	card->cardstate = CARD_DETECTED;
 
 	return 0;
 }
@@ -517,7 +716,7 @@
 static int capi_installed(void)
 {
 	int i;
-	for (i = 0; i < ncards; i++) {
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
 		if (cards[i].cardstate == CARD_RUNNING)
 			return 1;
 	}
@@ -526,6 +725,7 @@
 
 static __u16 capi_register(capi_register_params * rparam, __u16 * applidp)
 {
+	int nconn, want = rparam->level3cnt;
 	int i;
 	int appl;
 
@@ -544,14 +744,21 @@
 
 	memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params));
 
-	for (i = 0; i < ncards; i++) {
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		struct capi_profile *profp =
+			(struct capi_profile *)cards[i].version[VER_PROFILE];
+
 		if (cards[i].cardstate != CARD_RUNNING)
 			continue;
+
+		if (want > 0) nconn = want;
+		else nconn = profp->nbchannel * -want;
+		if (nconn == 0) nconn = profp->nbchannel;
+
 		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);
+			1024 * (nconn+1), nconn,
+			APPL(appl)->rparam.datablkcnt,
+			APPL(appl)->rparam.datablklen);
 	}
 	*applidp = appl;
 	printk(KERN_INFO "b1capi: appl %d up\n", appl);
@@ -564,20 +771,18 @@
 	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++) {
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
 		if (cards[i].cardstate != CARD_RUNNING) {
 			continue;
 		}
 		APPL(applid)->releasing++;
 		B1_send_release(cards[i].port, applid);
 	}
-	if (APPL(applid)->releasing == 0) {
+	if (APPL(applid)->releasing <= 0) {
 	        APPL(applid)->signal = 0;
 		APPL_MARK_FREE(applid);
 		printk(KERN_INFO "b1capi: appl %d down\n", applid);
@@ -661,7 +866,7 @@
 	if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) 
 		return 0x2002;
 
-	memcpy((void *) verp, CARD(contr)->version[VER_SERIAL],
+	memcpy((void *) verp, &CARD(contr)->cversion,
 	       sizeof(capi_version));
 	return CAPI_NOERROR;
 }
@@ -698,33 +903,87 @@
 static int capi_manufacturer(unsigned int cmd, void *data)
 {
 	unsigned long flags;
-	avmb1_loaddef ldef;
-	avmb1_carddef cdef;
+	avmb1_loadandconfigdef ldef;
+	avmb1_extcarddef cdef;
 	avmb1_resetdef rdef;
+	avmb1_getdef gdef;
 	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;
+	case AVMB1_ADDCARD_WITH_TYPE:
+		if (cmd == AVMB1_ADDCARD) {
+		   if ((rc = copy_from_user((void *) &cdef, data,
+					    sizeof(avmb1_carddef))))
+			   return rc;
+		   cdef.cardtype = AVM_CARDTYPE_B1;
+		} else {
+		   if ((rc = copy_from_user((void *) &cdef, data,
+					    sizeof(avmb1_extcarddef))))
+			   return rc;
+		}
 
-		if ((rc = avmb1_probecard(cdef.port, cdef.irq)) != 0)
+		if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0)
 			return rc;
 
-		return avmb1_addcard(cdef.port, cdef.irq);
+                if (cdef.cardtype == AVM_CARDTYPE_T1) {
+			int i;
+		        for (i=0; i < CAPI_MAXCONTR; i++) {
+	        	    	if (   cards[i].cardstate != CARD_FREE
+				    && cards[i].cardtype == AVM_CARDTYPE_T1
+				    && cards[i].cardnr == cdef.cardnr) {
+					printk(KERN_ERR
+						"b1capi: T1-HEMA-card-%d already at 0x%x\n",
+						cdef.cardnr, cards[i].port);
+					return -EBUSY;
+				}
+                        }
+			rc = T1_detectandinit(cdef.port,cdef.irq,cdef.cardnr);
+			if (rc) {
+			        printk(KERN_NOTICE "b1capi: NO T1-HEMA-card-%d at 0x%x (%d)\n",
+					  cdef.cardnr, cdef.port, rc);
+				return -EIO;
+                        }
+			printk(KERN_NOTICE "b1capi: T1-HEMA-card-%d at 0x%x\n",
+				  cdef.cardnr, cdef.port);
+		}
+
+		rc = avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype);
+		if (rc < 0)
+			return rc;
+		/* don't want to change interface t
+		   addcard/probecard/registercard */
+                if (cdef.cardtype == AVM_CARDTYPE_T1) {
+			int i;
+		        for (i=0; i < CAPI_MAXCONTR; i++) {
+	        	    	if (cards[i].cnr == rc) {
+					cards[i].cardnr = cdef.cardnr;
+					break;
+				}
+                        }
+		}
+		return rc;
 
 	case AVMB1_LOAD:
+	case AVMB1_LOAD_AND_CONFIG:
 
-		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);
+		if (cmd == AVMB1_LOAD) {
+			if ((rc = copy_from_user((void *) &ldef, data,
+						sizeof(avmb1_loaddef))))
+				return rc;
+			ldef.t4config.len = 0;
+			ldef.t4config.data = 0;
+		} else {
+			if ((rc = copy_from_user((void *) &ldef, data,
+					    	sizeof(avmb1_loadandconfigdef))))
+				return rc;
+		}
+		if (!VALID_CARD(ldef.contr))
+			return -ESRCH;
+
+		if (ldef.t4file.len <= 0) {
+			printk(KERN_DEBUG "b1capi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
 			return -EINVAL;
 		}
 
@@ -746,13 +1005,34 @@
 		}
 
 		B1_reset(card->port);
+
+		if (loaddebug) {
+			printk(KERN_DEBUG "b1capi: loading contr %d\n",
+				ldef.contr);
+		}
+
 		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 (ldef.t4config.len > 0) { /* load config */
+		        if (loaddebug) {
+				printk(KERN_DEBUG "b1capi: loading config to contr %d\n",
+				   			ldef.contr);
+		   	}
+			if ((rc = B1_load_config(card->port, &ldef.t4config))) {
+				B1_reset(card->port);
+				printk(KERN_ERR "b1capi: failed to load config!!\n");
+				card->cardstate = CARD_DETECTED;
+				return rc;
+			}
+		}
+
 		if (loaddebug) {
 			printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n",
 				ldef.contr);
@@ -770,8 +1050,7 @@
 		card->cardstate = CARD_INITSTATE;
 		save_flags(flags);
 		cli();
-		B1_assign_irq(card->port, card->irq);
-		B1_enable_irq(card->port);
+		B1_setinterrupt(card->port, card->irq, card->cardtype);
 		restore_flags(flags);
 
 		if (loaddebug) {
@@ -782,7 +1061,14 @@
 		/*
 		 * init card
 		 */
-		B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1);
+                if (card->cardtype == AVM_CARDTYPE_T1)
+		   B1_send_init(card->port, AVM_NAPPS,
+					    AVM_NNCCI_PER_CHANNEL*30,
+				   	    card->cnr - 1);
+		else
+		   B1_send_init(card->port, AVM_NAPPS,
+					    AVM_NNCCI_PER_CHANNEL*2,
+				   	    card->cnr - 1);
 
 		if (loaddebug) {
 			printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n",
@@ -799,25 +1085,45 @@
 				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;
+		return avmb1_resetcard(rdef.contr);
 
-		card = CARD(rdef.contr);
+	case AVMB1_GET_CARDINFO:
+		if ((rc = copy_from_user((void *) &gdef, data,
+					 sizeof(avmb1_getdef))))
+			return rc;
 
-		if (card->cardstate == CARD_RUNNING)
-			avmb1_card_down(card);
+		if (!VALID_CARD(gdef.contr))
+			return -ESRCH;
 
-		card->cardstate = CARD_DETECTED;
+		card = CARD(gdef.contr);
 
-		B1_reset(card->port);
-		B1_reset(card->port);
+		gdef.cardstate = card->cardstate;
+		gdef.cardtype = card->cardtype;
+
+		if ((rc = copy_to_user(data, (void *) &gdef,
+					 sizeof(avmb1_getdef))))
+			return rc;
 
 		return 0;
+	case AVMB1_REMOVECARD:
+		if ((rc = copy_from_user((void *) &rdef, data,
+					 sizeof(avmb1_resetdef))))
+			return rc;
+		if (!VALID_CARD(rdef.contr))
+			return -ESRCH;
+
+		card = CARD(rdef.contr);
+
+		if (card->cardstate != CARD_DETECTED)
+			return -EBUSY;
+
+		return avmb1_unregistercard(rdef.contr, 1);
 	}
 	return -EINVAL;
 }
@@ -855,6 +1161,7 @@
 	userp->next = capi_users;
 	capi_users = userp;
 	MOD_INC_USE_COUNT;
+	printk(KERN_NOTICE "b1capi: %s attached\n", userp->name);
 
 	return &avmb1_interface;
 }
@@ -868,6 +1175,7 @@
 			*pp = userp->next;
 			userp->next = 0;
 			MOD_DEC_USE_COUNT;
+			printk(KERN_NOTICE "b1capi: %s detached\n", userp->name);
 			return 0;
 		}
 	}
@@ -884,6 +1192,10 @@
 EXPORT_SYMBOL(detach_capi_interface);
 EXPORT_SYMBOL(avmb1_addcard);
 EXPORT_SYMBOL(avmb1_probecard);
+EXPORT_SYMBOL(avmb1_registercard);
+EXPORT_SYMBOL(avmb1_unregistercard);
+EXPORT_SYMBOL(avmb1_resetcard);
+EXPORT_SYMBOL(avmb1_detectcard);
 #else
 static struct symbol_table capidev_syms =
 {
@@ -892,6 +1204,10 @@
 	X(detach_capi_interface),
 	X(avmb1_addcard),
 	X(avmb1_probecard),
+	X(avmb1_registercard),
+	X(avmb1_unregistercard),
+	X(avmb1_resetcard),
+	X(avmb1_detectcard),
 #include <linux/symtab_end.h>
 };
 #endif
@@ -910,7 +1226,6 @@
 	char *p;
 	char rev[10];
 
-
 #ifndef HAS_NEW_SYMTAB
 	/* No symbols to export, hide all symbols */
 	register_symtab(&capidev_syms);
@@ -933,15 +1248,7 @@
 		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);
-	}
+        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
@@ -963,20 +1270,20 @@
 		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);
-
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		if (cards[i].cardstate != CARD_FREE) {
+			/*
+			 * disable card
+			 */
+			B1_disable_irq(cards[i].port);
+			avmb1_resetcard(i+1);
+			/*
+			 * free kernel resources
+			 */
+			avmb1_unregistercard(i+1, 1);
+		}
 	}
+	schedule(); /* execute queued tasks .... */
 	printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev);
 }
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/b1lli.c linux/drivers/isdn/avmb1/b1lli.c
--- v2.0.35/linux/drivers/isdn/avmb1/b1lli.c	Thu Aug 21 15:14:34 1997
+++ linux/drivers/isdn/avmb1/b1lli.c	Sun Nov 15 10:32:57 1998
@@ -1,11 +1,48 @@
 /*
- * $Id: b1lli.c,v 1.1.2.1 1997/07/13 12:16:46 calle Exp $
+ * $Id: b1lli.c,v 1.1.2.11 1998/10/25 14:36:18 fritz 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.11  1998/10/25 14:36:18  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.1.2.10  1998/03/20 20:34:41  calle
+ * port valid check now only for T1, because of the PCI and PCMCIA cards.
+ *
+ * Revision 1.1.2.9  1998/03/20 14:38:20  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.1.2.8  1998/03/18 17:43:29  calle
+ * T1 with fastlink, bugfix for multicontroller support in capidrv.c
+ *
+ * Revision 1.1.2.7  1998/03/04 17:33:50  calle
+ * Changes for T1.
+ *
+ * Revision 1.1.2.6  1998/02/27 15:40:44  calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
+ * Revision 1.1.2.5  1998/02/13 16:28:28  calle
+ * first step for T1
+ *
+ * Revision 1.1.2.4  1998/01/27 16:12:51  calle
+ * Support for PCMCIA B1/M1/M2 ready.
+ *
+ * Revision 1.1.2.3  1998/01/15 15:33:37  calle
+ * print cardtype, d2 protocol and linetype after load.
+ *
+ * Revision 1.1.2.2  1997/11/26 10:46:55  calle
+ * prepared for M1 (Mobile) and T1 (PMX) cards.
+ * prepared to set configuration after load to support other D-channel
+ * protocols, point-to-point and leased lines.
+ *
  * Revision 1.1.2.1  1997/07/13 12:16:46  calle
  * bug fix for more than one controller in connect_req.
  *
@@ -20,6 +57,7 @@
  *
  * 
  */
+/* #define FASTLINK_DEBUG */
 
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
@@ -34,6 +72,8 @@
 #include "capicmd.h"
 #include "capiutil.h"
 
+extern int showcapimsgs;
+
 /*
  * LLI Messages to the ISDN-ControllerISDN Controller 
  */
@@ -69,6 +109,11 @@
 					   * B3Length data .... 
 					 */
 
+#define SEND_CONFIG		0x21    /*
+                                         */
+
+#define SEND_POLLACK		0x73    /* T1 Watchdog */
+
 /*
  * LLI Messages from the ISDN-ControllerISDN Controller 
  */
@@ -112,6 +157,13 @@
 #define RECEIVE_RELEASE		0x26	/*
 					   * int32 AppllID int32 0xffffffff 
 					 */
+#define RECEIVE_TASK_READY	0x31	/*
+					   * int32 tasknr
+					   * int32 Length Taskname ...
+					 */
+
+#define WRITE_REGISTER		0x00
+#define READ_REGISTER		0x01
 
 /*
  * port offsets
@@ -124,9 +176,47 @@
 #define B1_RESET		0x10
 #define B1_ANALYSE		0x04
 
+/* Hema card T1 */
+
+#define T1_FASTLINK		0x00
+#define T1_SLOWLINK		0x08
+
+#define T1_READ			B1_READ
+#define T1_WRITE		B1_WRITE
+#define T1_INSTAT		B1_INSTAT
+#define T1_OUTSTAT		B1_OUTSTAT
+#define T1_IRQENABLE		0x05
+#define T1_FIFOSTAT		0x06
+#define T1_RESETLINK		0x10
+#define T1_ANALYSE		0x11
+#define T1_IRQMASTER		0x12
+#define T1_IDENT		0x17
+#define T1_RESETBOARD		0x1f
+
+#define	T1F_IREADY		0x01
+#define	T1F_IHALF		0x02
+#define	T1F_IFULL		0x04
+#define	T1F_IEMPTY		0x08
+#define	T1F_IFLAGS		0xF0
+
+#define	T1F_OREADY		0x10
+#define	T1F_OHALF		0x20
+#define	T1F_OEMPTY		0x40
+#define	T1F_OFULL		0x80
+#define	T1F_OFLAGS		0xF0
+
+/* there are HEMA cards with 1k and 4k FIFO out */
+#define FIFO_OUTBSIZE		256
+#define FIFO_INPBSIZE		512
 
+#define HEMA_VERSION_ID		0
+#define HEMA_PAL_ID		0
 
-static inline unsigned char b1outp(unsigned short base,
+#define B1_STAT0(cardtype)  ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l)
+#define B1_STAT1(cardtype)  (0x80E00000l)
+
+
+static inline unsigned char b1outp(unsigned int base,
 				   unsigned short offset,
 				   unsigned char value)
 {
@@ -134,6 +224,191 @@
 	return inb(base + B1_ANALYSE);
 }
 
+static inline void t1outp(unsigned int base,
+			  unsigned short offset,
+			  unsigned char value)
+{
+	outb(value, base + offset);
+}
+
+static inline unsigned char t1inp(unsigned int base,
+			          unsigned short offset)
+{
+	return inb(base + offset);
+}
+
+static inline int B1_isfastlink(unsigned int base)
+{
+	return (inb(base + T1_IDENT) & ~0x82) == 1;
+}
+static inline unsigned char B1_fifostatus(unsigned int base)
+{
+	return inb(base + T1_FIFOSTAT);
+}
+
+static inline int B1_rx_full(unsigned int base)
+{
+	return inb(base + B1_INSTAT) & 0x1;
+}
+
+static inline unsigned char B1_get_byte(unsigned int base)
+{
+	unsigned long i = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	while (!B1_rx_full(base) && i > jiffies);
+	if (B1_rx_full(base))
+		return inb(base + B1_READ);
+	printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
+	return 0;
+}
+
+static inline unsigned int B1_get_word(unsigned int 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 int base)
+{
+	return inb(base + B1_OUTSTAT) & 0x1;
+}
+
+static inline void B1_put_byte(unsigned int base, unsigned char val)
+{
+	while (!B1_tx_empty(base));
+	b1outp(base, B1_WRITE, val);
+}
+
+static inline void B1_put_word(unsigned int 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 int base,
+					unsigned char *dp)
+{
+	unsigned int len, i;
+#ifdef FASTLINK_DEBUG
+	unsigned wcnt = 0, bcnt = 0;
+#endif
+
+	len = i = B1_get_word(base);
+        if (B1_isfastlink(base)) {
+		int status;
+		while (i > 0) {
+			status = B1_fifostatus(base) & (T1F_IREADY|T1F_IHALF);
+			if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
+
+			switch (status) {
+				case T1F_IREADY|T1F_IHALF|T1F_IFULL:
+					insb(base+B1_READ, dp, FIFO_INPBSIZE);
+					dp += FIFO_INPBSIZE;
+					i -= FIFO_INPBSIZE;
+#ifdef FASTLINK_DEBUG
+					wcnt += FIFO_INPBSIZE;
+#endif
+					break;
+				case T1F_IREADY|T1F_IHALF: 
+					insb(base+B1_READ,dp, i);
+#ifdef FASTLINK_DEBUG
+					wcnt += i;
+#endif
+					dp += i;
+					i = 0;
+					if (i == 0)
+						break;
+					/* fall through */
+				default:
+					*dp++ = B1_get_byte(base);
+					i--;
+#ifdef FASTLINK_DEBUG
+					bcnt++;
+#endif
+					break;
+			}
+	    }
+#ifdef FASTLINK_DEBUG
+	    if (wcnt)
+	    printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
+				base, len, wcnt, bcnt);
+#endif
+	} else {
+		while (i-- > 0)
+			*dp++ = B1_get_byte(base);
+	}
+	return len;
+}
+
+static inline void B1_put_slice(unsigned int base,
+				unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	B1_put_word(base, i);
+        if (B1_isfastlink(base)) {
+		int status;
+		while (i > 0) {
+			status = B1_fifostatus(base) & (T1F_OREADY|T1F_OHALF);
+			if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
+			switch (status) {
+				case T1F_OREADY|T1F_OHALF|T1F_OEMPTY: 
+					outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE);
+					dp += FIFO_OUTBSIZE;
+					i -= FIFO_OUTBSIZE;
+					break;
+				case T1F_OREADY|T1F_OHALF: 
+					outsb(base+B1_WRITE, dp, i);
+					dp += i;
+					i = 0;
+				        break;
+				default:
+					B1_put_byte(base, *dp++);
+					i--;
+					break;
+			}
+		}
+	} else {
+		while (i-- > 0)
+			B1_put_byte(base, *dp++);
+	}
+}
+
+static void b1_wr_reg(unsigned int base,
+                      unsigned int reg,
+		      unsigned int value)
+{
+	B1_put_byte(base, WRITE_REGISTER);
+        B1_put_word(base, reg);
+        B1_put_word(base, value);
+}
+
+static inline unsigned int b1_rd_reg(unsigned int base,
+                                     unsigned int reg)
+{
+	B1_put_byte(base, READ_REGISTER);
+        B1_put_word(base, reg);
+        return B1_get_word(base);
+	
+}
+
+static inline void b1_set_test_bit(unsigned int base,
+				   int cardtype,
+				   int onoff)
+{
+    b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
+}
+
+static inline int b1_get_test_bit(unsigned int base,
+                                  int cardtype)
+{
+    return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
+}
+
 static int irq_table[16] =
 {0,
  0,
@@ -153,27 +428,92 @@
  112,				/* irq 15 */
 };
 
-int B1_valid_irq(unsigned irq)
+static int hema_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 0x80,				/* irq 3 */
+ 0,
+ 0x90,				/* irq 5 */
+ 0,
+ 0xA0,				/* irq 7 */
+ 0,
+ 0xB0,				/* irq 9 */
+ 0xC0,				/* irq 10 */
+ 0xD0,				/* irq 11 */
+ 0xE0,				/* irq 12 */
+ 0,
+ 0,
+ 0xF0,				/* irq 15 */
+};
+
+
+int B1_valid_irq(unsigned irq, int cardtype)
 {
-	return irq_table[irq] != 0;
+	switch (cardtype) {
+	   default:
+	   case AVM_CARDTYPE_M1:
+	   case AVM_CARDTYPE_M2:
+	   case AVM_CARDTYPE_B1:
+	   	return irq_table[irq & 0xf] != 0;
+	   case AVM_CARDTYPE_T1:
+	   	return hema_irq_table[irq & 0xf] != 0;
+	}
 }
 
-unsigned char B1_assign_irq(unsigned short base, unsigned irq)
+int B1_valid_port(unsigned port, int cardtype)
 {
-	return b1outp(base, B1_RESET, irq_table[irq]);
+   switch (cardtype) {
+	   default:
+	   case AVM_CARDTYPE_M1:
+	   case AVM_CARDTYPE_M2:
+	   case AVM_CARDTYPE_B1:
+#if 0	/* problem with PCMCIA and PCI cards */
+		switch (port) {
+			case 0x150:
+			case 0x250:
+			case 0x300:
+			case 0x340:
+				return 1;
+		}
+		return 0;
+#else
+		return 1;
+#endif
+	   case AVM_CARDTYPE_T1:
+		return ((port & 0x7) == 0) && ((port & 0x30) != 0x30);
+   }
+}
+
+void B1_setinterrupt(unsigned int base,
+			         unsigned irq, int cardtype)
+{
+	switch (cardtype) {
+	   case AVM_CARDTYPE_T1:
+              t1outp(base, B1_INSTAT, 0x00);
+              t1outp(base, B1_INSTAT, 0x02);
+	      t1outp(base, T1_IRQMASTER, 0x08);
+	   default:
+	   case AVM_CARDTYPE_M1:
+	   case AVM_CARDTYPE_M2:
+	   case AVM_CARDTYPE_B1:
+	      b1outp(base, B1_INSTAT, 0x00);
+	      b1outp(base, B1_RESET, irq_table[irq]);
+	      b1outp(base, B1_INSTAT, 0x02);
+	 }
 }
 
-unsigned char B1_enable_irq(unsigned short base)
+unsigned char B1_disable_irq(unsigned int base)
 {
-	return b1outp(base, B1_INSTAT, 0x02);
+	return b1outp(base, B1_INSTAT, 0x00);
 }
 
-unsigned char B1_disable_irq(unsigned short base)
+void T1_disable_irq(unsigned int base)
 {
-	return b1outp(base, B1_INSTAT, 0x00);
+      t1outp(base, T1_IRQMASTER, 0x00);
 }
 
-void B1_reset(unsigned short base)
+void B1_reset(unsigned int base)
 {
 	b1outp(base, B1_RESET, 0);
 	udelay(55 * 2 * 1000);	/* 2 TIC's */
@@ -185,24 +525,39 @@
 	udelay(55 * 2 * 1000);	/* 2 TIC's */
 }
 
-int B1_detect(unsigned short base)
+void T1_reset(unsigned int base)
+{
+        /* reset T1 Controller */
+        B1_reset(base);
+        /* disable irq on HEMA */
+        t1outp(base, B1_INSTAT, 0x00);
+        t1outp(base, B1_OUTSTAT, 0x00);
+        t1outp(base, T1_IRQMASTER, 0x00);
+        /* reset HEMA board configuration */
+	t1outp(base, T1_RESETBOARD, 0xf);
+}
+
+int B1_detect(unsigned int base, int cardtype)
 {
+	int onoff, i;
+
+	if (cardtype == AVM_CARDTYPE_T1)
+	   return 0;
+
 	/*
 	 * 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);
+	/* b1outp(base, B1_OUTSTAT, 0x2); */
 	if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
-	    || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2)
+	    /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
 		return 2;
-
 	/*
 	 * Statusregister 0000 000x 
 	 */
@@ -211,76 +566,96 @@
 	if ((inb(base + B1_INSTAT) & 0xfe)
 	    || (inb(base + B1_OUTSTAT) & 0xfe))
 		return 3;
+        
+	for (onoff = !0, i= 0; i < 10 ; i++) {
+		b1_set_test_bit(base, cardtype, onoff);
+		if (b1_get_test_bit(base, cardtype) != onoff)
+		   return 4;
+		onoff = !onoff;
+	}
 
-	return 0;
-}
+	if (cardtype == AVM_CARDTYPE_M1)
+	   return 0;
 
-static inline int B1_rx_full(unsigned short base)
-{
-	return inb(base + B1_INSTAT) & 0x1;
-}
+        if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
+	   return 5;
 
-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)
+int T1_detectandinit(unsigned int base, unsigned irq, int cardnr)
 {
-	while (!B1_tx_empty(base));
-	b1outp(base, B1_WRITE, val);
-}
+	unsigned char cregs[8];
+	unsigned char reverse_cardnr;
+	unsigned long flags;
+	unsigned char dummy;
+	int i;
 
-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);
-}
+	reverse_cardnr =   ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
+		         | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
+	cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
+	cregs[1] = 0x00; /* fast & slow link connected to CON1 */
+	cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
+	cregs[3] = 0;
+	cregs[4] = 0x11; /* zero wait state */
+	cregs[5] = hema_irq_table[irq & 0xf];
+	cregs[6] = 0;
+	cregs[7] = 0;
 
-static inline unsigned int B1_get_slice(unsigned short base,
-					unsigned char *dp)
-{
-	unsigned int len, i;
+	save_flags(flags);
+	cli();
+	/* board reset */
+	t1outp(base, T1_RESETBOARD, 0xf);
+	udelay(100 * 1000);
+	dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */
+
+	/* write config */
+	dummy = (base >> 4) & 0xff;
+	for (i=1;i<=0xf;i++) t1outp(base, i, dummy);
+	t1outp(base, HEMA_PAL_ID & 0xf, dummy);
+	t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
+	for(i=1;i<7;i++) t1outp(base, 0, cregs[i]);
+	t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
+	restore_flags(flags);
 
-	len = i = B1_get_word(base);
-	while (i-- > 0)
-		*dp++ = B1_get_byte(base);
-	return len;
-}
+	udelay(100 * 1000);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+	udelay(10 * 1000);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 1);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1);
+	udelay(100 * 1000);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+	udelay(10 * 1000);
+	t1outp(base, T1_FASTLINK+T1_ANALYSE, 0);
+	udelay(5 * 1000);
+	t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0);
 
-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++);
+	if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+		return 1;
+	if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */
+		return 2;
+	if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0)
+		return 3;
+	if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70)
+		return 4;
+	if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0)
+		return 5;
+	if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1)
+		return 6;
+	if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+		return 7;
+	if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0)
+		return 8;
+	if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0)
+		return 9;
+        return 0;
 }
 
 extern int loaddebug;
 
-int B1_load_t4file(unsigned short base, avmb1_t4file * t4file)
+int B1_load_t4file(unsigned int base, avmb1_t4file * t4file)
 {
 	/*
 	 * Data is in user space !!!
@@ -319,7 +694,63 @@
 	return 0;
 }
 
-int B1_loaded(unsigned short base)
+int B1_load_config(unsigned int base, avmb1_t4file * config)
+{
+	/*
+	 * Data is in user space !!!
+	 */
+	unsigned char buf[256];
+	unsigned char *dp;
+	int i, j, left, retval;
+
+
+	dp = config->data;
+	left = config->len;
+	if (left) {
+		B1_put_byte(base, SEND_CONFIG);
+        	B1_put_word(base, 1);
+		B1_put_byte(base, SEND_CONFIG);
+        	B1_put_word(base, left);
+	}
+	while (left > sizeof(buf)) {
+		retval = copy_from_user(buf, dp, sizeof(buf));
+		if (retval)
+			return -EFAULT;
+		if (loaddebug)
+			printk(KERN_DEBUG "b1capi: conf load: %d bytes ..", sizeof(buf));
+		for (i = 0; i < sizeof(buf); ) {
+			B1_put_byte(base, SEND_CONFIG);
+			for (j=0; j < 4; j++) {
+				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: conf load: %d bytes ..", left);
+		for (i = 0; i < left; ) {
+			B1_put_byte(base, SEND_CONFIG);
+			for (j=0; j < 4; j++) {
+				if (i < left)
+					B1_put_byte(base, buf[i++]);
+				else
+					B1_put_byte(base, 0);
+			}
+		}
+		if (loaddebug)
+		   printk("ok\n");
+	}
+	return 0;
+}
+
+int B1_loaded(unsigned int base)
 {
 	int i;
 	unsigned char ans;
@@ -331,7 +762,7 @@
 			break;
 	}
 	if (!B1_tx_empty(base)) {
-		printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n");
+		printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout tx\n", base);
 		return 0;
 	}
 	B1_put_byte(base, SEND_POLL);
@@ -343,11 +774,12 @@
 					printk(KERN_DEBUG "b1capi: loaded: ok\n");
 				return 1;
 			}
-			printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans);
+			printk(KERN_ERR "b1lli(0x%x): B1_loaded: got 0x%x ???\n",
+				base, ans);
 			return 0;
 		}
 	}
-	printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n");
+	printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout rx\n", base);
 	return 0;
 }
 
@@ -368,7 +800,7 @@
  * ------------------------------------------------------------------- 
  */
 
-void B1_send_init(unsigned short port,
+void B1_send_init(unsigned int port,
 	     unsigned int napps, unsigned int nncci, unsigned int cardnr)
 {
 	unsigned long flags;
@@ -382,7 +814,7 @@
 	restore_flags(flags);
 }
 
-void B1_send_register(unsigned short port,
+void B1_send_register(unsigned int port,
 		      __u16 appid, __u32 nmsg,
 		      __u32 nb3conn, __u32 nb3blocks, __u32 b3bsize)
 {
@@ -399,7 +831,7 @@
 	restore_flags(flags);
 }
 
-void B1_send_release(unsigned short port,
+void B1_send_release(unsigned int port,
 		     __u16 appid)
 {
 	unsigned long flags;
@@ -411,9 +843,7 @@
 	restore_flags(flags);
 }
 
-extern int showcapimsgs;
-
-void B1_send_message(unsigned short port, struct sk_buff *skb)
+void B1_send_message(unsigned int port, struct sk_buff *skb)
 {
 	unsigned long flags;
 	__u16 len = CAPIMSG_LEN(skb->data);
@@ -479,6 +909,7 @@
 	unsigned NCCI;
 	unsigned WindowSize;
 
+t1retry:
 	if (!B1_rx_full(card->port))
 		return;
 
@@ -555,7 +986,7 @@
 		WindowSize = B1_get_word(card->port);
 
 		if (showcapimsgs)
-			printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+			printk(KERN_DEBUG "b1lli(0x%x): NEW_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI);
 
 		avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize);
 
@@ -567,19 +998,23 @@
 		NCCI = B1_get_word(card->port);
 
 		if (showcapimsgs)
-			printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+			printk(KERN_DEBUG "b1lli(0x%x): FREE_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI);
 
 		avmb1_handle_free_ncci(card, ApplId, NCCI);
 		break;
 
 	case RECEIVE_START:
+                if (card->cardtype == AVM_CARDTYPE_T1) {
+	           B1_put_byte(card->port, SEND_POLLACK);
+		   /* printk(KERN_DEBUG "b1lli: T1 watchdog\n"); */
+                }
 		if (card->blocked)
-			printk(KERN_DEBUG "b1lli: RESTART\n");
+			printk(KERN_DEBUG "b1lli(0x%x): RESTART\n", card->port);
 		card->blocked = 0;
 		break;
 
 	case RECEIVE_STOP:
-		printk(KERN_DEBUG "b1lli: STOP\n");
+		printk(KERN_DEBUG "b1lli(0x%x): STOP\n", card->port);
 		card->blocked = 1;
 		break;
 
@@ -588,14 +1023,24 @@
 		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",
+		printk(KERN_INFO "b1lli(0x%x): %s-card (%s) now active\n",
+		       card->port,
 		       card->version[VER_CARDTYPE],
-		       card->version[VER_DRIVER],
-		       card->version[VER_PROTO]);
+		       card->version[VER_DRIVER]);
 		avmb1_card_ready(card);
 		break;
+        case RECEIVE_TASK_READY:
+		ApplId = (unsigned) B1_get_word(card->port);
+		MsgLen = B1_get_slice(card->port, card->msgbuf);
+		card->msgbuf[MsgLen] = 0;
+		printk(KERN_INFO "b1lli(0x%x): Task %d \"%s\" ready.\n",
+				card->port, ApplId, card->msgbuf);
+		break;
 	default:
-		printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd);
+		printk(KERN_ERR "b1lli(0x%x): B1_handle_interrupt: 0x%x ???\n",
+				card->port, b1cmd);
 		break;
 	}
+	if (card->cardtype == AVM_CARDTYPE_T1) 
+		goto t1retry;
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/b1pci.c linux/drivers/isdn/avmb1/b1pci.c
--- v2.0.35/linux/drivers/isdn/avmb1/b1pci.c	Mon Aug  4 17:33:59 1997
+++ linux/drivers/isdn/avmb1/b1pci.c	Sun Nov 15 10:32:57 1998
@@ -1,11 +1,20 @@
 /*
- * $Id: b1pci.c,v 1.2 1997/05/18 09:24:13 calle Exp $
+ * $Id: b1pci.c,v 1.2.2.2 1998/01/23 16:49:30 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.2.2  1998/01/23 16:49:30  calle
+ * added functions for pcmcia cards,
+ * avmb1_addcard returns now the controller number.
+ *
+ * Revision 1.2.2.1  1997/11/26 10:46:57  calle
+ * prepared for M1 (Mobile) and T1 (PMX) cards.
+ * prepared to set configuration after load to support other D-channel
+ * protocols, point-to-point and leased lines.
+ *
  * Revision 1.2  1997/05/18 09:24:13  calle
  * added verbose disconnect reason reporting to avmb1.
  * some fixes in capi20 interface.
@@ -34,7 +43,7 @@
 #define PCI_DEVICE_ID_AVM_B1	0x700
 #endif
 
-static char *revision = "$Revision: 1.2 $";
+static char *revision = "$Revision: 1.2.2.2 $";
 
 /* ------------------------------------------------------------- */
 
@@ -98,13 +107,13 @@
 		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) {
+		if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1)) != 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)
+		if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1)) < 0)
 			return rc;
 	}
 	return 0;
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/capidrv.c linux/drivers/isdn/avmb1/capidrv.c
--- v2.0.35/linux/drivers/isdn/avmb1/capidrv.c	Thu Aug 21 15:14:34 1997
+++ linux/drivers/isdn/avmb1/capidrv.c	Sun Nov 15 10:32:57 1998
@@ -1,11 +1,49 @@
 /*
- * $Id: capidrv.c,v 1.3.2.1 1997/07/13 12:16:48 calle Exp $
+ * $Id: capidrv.c,v 1.3.2.12 1998/09/11 15:37:11 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.12  1998/09/11 15:37:11  calle
+ * Started with support for CAPI channel allocation/bundling.
+ *
+ * Revision 1.3.2.11  1998/04/02 10:27:59  calle
+ * version check for D2 trace was wrong :-(
+ *
+ * Revision 1.3.2.10  1998/03/20 14:38:24  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.3.2.9  1998/03/20 09:01:12  calle
+ * Changes capi_register handling to get full support for 30 bchannels.
+ *
+ * Revision 1.3.2.8  1998/03/18 17:51:28  calle
+ * added controller number to error messages
+ *
+ * Revision 1.3.2.7  1998/02/27 15:40:47  calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
+ * Revision 1.3.2.6  1998/02/02 19:51:13  calle
+ * Fixed vbox (audio) acceptb.
+ *
+ * Revision 1.3.2.5  1997/10/29 09:35:29  calle
+ * correct byteorder problem with new isdnlog interface.
+ *
+ * Revision 1.3.2.4  1997/10/26 15:04:24  calle
+ * prepared isdnlog interface for d2-trace in newer firmware.
+ *
+ * Revision 1.3.2.3  1997/10/24 06:37:00  calle
+ * changed LISTEN cipmask, now we can distinguish voice, fax und data calls.
+ *
+ * Revision 1.3.2.2  1997/10/08 05:42:25  calle
+ * Added isdnlog support. patch to isdnlog needed.
+ *
  * Revision 1.3.2.1  1997/07/13 12:16:48  calle
  * bug fix for more than one controller in connect_req.
  *
@@ -45,13 +83,14 @@
 #include <linux/isdnif.h>
 #include <linux/capi.h>
 #include <linux/kernelcapi.h>
+#include <linux/ctype.h>
 
 #include "compat.h"
 #include "capiutil.h"
 #include "capicmd.h"
 #include "capidrv.h"
 
-static char *revision = "$Revision: 1.3.2.1 $";
+static char *revision = "$Revision: 1.3.2.12 $";
 int debugmode = 0;
 
 #ifdef HAS_NEW_SYMTAB
@@ -109,6 +148,7 @@
 			__u16 msgid;	/* to identfy CONNECT_CONF */
 			int chan;
 			int state;
+			int leasedline;
 			struct capidrv_ncci {
 				struct capidrv_ncci *next;
 				struct capidrv_plci *plcip;
@@ -125,8 +165,15 @@
 	} *bchans;
 
 	struct capidrv_plci *plci_list;
+
+	/* for q931 data */
+	__u8  q931_buf[4096];
+	__u8 *q931_read;
+	__u8 *q931_write;
+	__u8 *q931_end;
 };
 
+
 struct capidrv_data {
 	__u16 appid;
 	int ncontr;
@@ -144,6 +191,9 @@
 static capidrv_data global;
 static struct capi_interface *capifuncs;
 
+static void handle_dtrace_data(capidrv_contr *card,
+	int send, int level2, __u8 *data, __u16 len);
+
 /* -------- convert functions ---------------------------------------- */
 
 static inline __u32 b1prot(int l2, int l3)
@@ -170,9 +220,8 @@
 	default:
 		return 0;
 	case ISDN_PROTO_L2_HDLC:
-		return 1;
 	case ISDN_PROTO_L2_TRANS:
-		return 0;
+		return 1;
 	}
 }
 
@@ -338,8 +387,8 @@
 			return;
 		}
 	}
-	printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n",
-	       plcip, plcip->plci);
+	printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n",
+	       card->contrnr, plcip, plcip->plci);
 }
 
 /* -------- ncci management ------------------------------------------ */
@@ -437,15 +486,15 @@
 
 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},
-	{},
+  {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)
@@ -454,15 +503,15 @@
 	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);
+				printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n",
+				       card->contrnr, 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);
+	printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n",
+	       card->contrnr, card->state, event);
 
 }
 
@@ -492,46 +541,57 @@
 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},
+  {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},
+  {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, 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},
+  {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},
+  {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},
+  {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},
+  {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, 0},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_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},
+  {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},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_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},
+  {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},
+  {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},
+  {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
   /* P-6 */
-	{ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
-	{},
+  {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
+  /* P-0.Res */
+  {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0},
+  {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, 0},
+  /* P-RES */
+  {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, 0},
+  /* P-HELD */
+  {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, 0},
+  {},
 };
 
 static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event)
@@ -540,8 +600,8 @@
 	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);
+				printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n",
+				  card->contrnr, plci->plci, plci->state, p->nextstate);
 			plci->state = p->nextstate;
 			if (p->changefunc)
 				p->changefunc(card, plci);
@@ -549,8 +609,8 @@
 		}
 		p++;
 	}
-	printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n",
-	       plci->plci, plci->state, event);
+	printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n",
+	       card->contrnr, plci->plci, plci->state, event);
 }
 
 /* ------------------------------------------------------------------ */
@@ -567,7 +627,7 @@
 				 ncci->plcip->plci,
 				 0,	/* BChannelinformation */
 				 0,	/* Keypadfacility */
-				 0,	/* Useruserdata */
+				 0,	/* Useruserdata */   /* $$$$ */
 				 0	/* Facilitydataarray */
 	);
 	send_message(card, &cmsg);
@@ -592,34 +652,35 @@
 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},
+  {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},
+  {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, n0},
   /* 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_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},
+  {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},
+  {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},
+  {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
+  {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_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},
+  {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},
+  {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},
-	{},
+  {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)
@@ -628,8 +689,8 @@
 	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);
+				printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n",
+				  card->contrnr, ncci->ncci, ncci->state, p->nextstate);
 			if (p->nextstate == ST_NCCI_PREVIOUS) {
 				ncci->state = ncci->oldstate;
 				ncci->oldstate = p->actstate;
@@ -643,8 +704,8 @@
 		}
 		p++;
 	}
-	printk(KERN_ERR "capidrv: ncci_change_state:0x%x state=%d event=%d ????\n",
-	       ncci->ncci, ncci->state, event);
+	printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n",
+	       card->contrnr, ncci->ncci, ncci->state, event);
 }
 
 /* ------------------------------------------------------------------- */
@@ -677,8 +738,8 @@
 
 	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);
+			printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
+			       card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
 		if (cmsg->Info) {
 			listen_change_state(card, EV_LISTEN_CONF_ERROR);
 		} else if (card->cipmask == 0) {
@@ -689,8 +750,55 @@
 		break;
 
 	case CAPI_MANUFACTURER_IND:	/* Controller */
+		if (   cmsg->ManuID == 0x214D5641
+		    && cmsg->Class == 0
+		    && cmsg->Function == 1) {
+		   __u8  *data = cmsg->ManuData+3;
+		   __u16  len = cmsg->ManuData[0];
+		   __u16 layer;
+		   int direction;
+		   if (len == 255) {
+		      len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8));
+		      data += 2;
+		   }
+		   len -= 2;
+		   layer = ((*(data-1)) << 8) | *(data-2);
+		   if (layer & 0x300)
+			direction = (layer & 0x200) ? 0 : 1;
+		   else direction = (layer & 0x800) ? 0 : 1;
+		   if (layer & 0x0C00) {
+		   	if ((layer & 0xff) == 0x80) {
+		           handle_dtrace_data(card, direction, 1, data, len);
+		           break;
+		   	}
+		   } else if ((layer & 0xff) < 0x80) {
+		      handle_dtrace_data(card, direction, 0, data, len);
+		      break;
+		   }
+	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n",
+                        card->contrnr, 
+			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			cmsg->adr.adrController, layer);
+                   break;
+		}
 		goto ignored;
 	case CAPI_MANUFACTURER_CONF:	/* Controller */
+		if (cmsg->ManuID == 0x214D5641) {
+		   char *s = 0;
+		   switch (cmsg->Class) {
+		      case 0: break;
+		      case 1: s = "unknown class"; break;
+		      case 2: s = "unknown function"; break;
+		      default: s = "unkown error"; break;
+		   }
+		   if (s)
+	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
+			card->contrnr,
+			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			cmsg->adr.adrController,
+			cmsg->Function, s);
+		   break;
+		}
 		goto ignored;
 	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
 		goto ignored;
@@ -702,14 +810,16 @@
 		goto ignored;
 
 	default:
-		printk(KERN_ERR "capidrv: got %s from controller 0x%x ???",
+		printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrController);
 	}
 	return;
 
       ignored:
-	printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n",
+	printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrController);
 }
@@ -722,12 +832,12 @@
 	int chan;
 
 	if ((chan = new_bchan(card)) == -1) {
-		printk(KERN_ERR "capidrv: incoming call on not existing bchan ?\n");
+		printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr);
 		return;
 	}
 	bchan = &card->bchans[chan];
 	if ((plcip = new_plci(card, chan)) == 0) {
-		printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n");
+		printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr);
 		return;
 	}
 	bchan->incoming = 1;
@@ -749,7 +859,8 @@
 	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", 
+	printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n", 
+			card->contrnr,
 			cmd.parm.setup.phone,
 			cmd.parm.setup.si1,
 			cmd.parm.setup.si2,
@@ -766,7 +877,8 @@
 		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",
+		printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n",
+			card->contrnr,
 			cmd.parm.setup.phone,
 			cmd.parm.setup.si1,
 			cmd.parm.setup.si2,
@@ -783,7 +895,8 @@
 		 * and CONNECT_RESP already sent.
 		 */
 		if (plcip->state == ST_PLCI_INCOMING) {
-			printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s tty alerting\n",
+			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n",
+				card->contrnr,
 				cmd.parm.setup.phone,
 				cmd.parm.setup.si1,
 				cmd.parm.setup.si2,
@@ -800,7 +913,8 @@
 			plcip->msgid = cmsg->Messagenumber;
 			send_message(card, cmsg);
 		} else {
-			printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n",
+			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n",
+				card->contrnr,
 				cmd.parm.setup.phone,
 				cmd.parm.setup.si1,
 				cmd.parm.setup.si2,
@@ -843,7 +957,8 @@
 
 	case CAPI_DISCONNECT_IND:	/* plci */
 		if (cmsg->Reason) {
-			printk(KERN_INFO "capidrv: %s reason 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI);
 		}
@@ -861,7 +976,8 @@
 
 	case CAPI_DISCONNECT_CONF:	/* plci */
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrPLCI);
@@ -874,7 +990,8 @@
 
 	case CAPI_ALERT_CONF:	/* plci */
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrPLCI);
@@ -887,7 +1004,8 @@
 
 	case CAPI_CONNECT_CONF:	/* plci */
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrPLCI);
@@ -920,7 +1038,7 @@
 			nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI);
 
 			if (!nccip) {
-				printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+				printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
 				break;	/* $$$$ */
 			}
 			capi_fill_CONNECT_B3_REQ(cmsg,
@@ -960,7 +1078,8 @@
 				break;
 			}
 		}
-		printk(KERN_ERR "capidrv: %s\n", capi_cmsg2str(cmsg));
+		printk(KERN_ERR "capidrv-%d: %s\n",
+				card->contrnr, capi_cmsg2str(cmsg));
 		break;
 
 	case CAPI_CONNECT_ACTIVE_CONF:		/* plci */
@@ -976,18 +1095,21 @@
 		goto ignored;
 
 	default:
-		printk(KERN_ERR "capidrv: got %s for plci 0x%x ???",
+		printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrPLCI);
 	}
 	return;
       ignored:
-	printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n",
+	printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrPLCI);
 	return;
       notfound:
-	printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n",
+	printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrPLCI);
 	return;
@@ -1021,8 +1143,8 @@
 		cmd.arg = nccip->chan;
 		card->interface.statcallb(&cmd);
 
-		printk(KERN_INFO "capidrv: chan %d up with ncci 0x%x\n",
-		       nccip->chan, nccip->ncci);
+		printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n",
+		       card->contrnr, nccip->chan, nccip->ncci);
 		break;
 
 	case CAPI_CONNECT_B3_ACTIVE_CONF:	/* ncci */
@@ -1046,9 +1168,10 @@
 				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP);
 				break;
 			}
-			printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+			printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n",							card->contrnr);
 		} else {
-			printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n",
+			printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->adr.adrNCCI);
 		}
@@ -1071,7 +1194,8 @@
 
 		nccip->ncci = cmsg->adr.adrNCCI;
 		if (cmsg->Info) {
-			printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrNCCI);
@@ -1118,7 +1242,8 @@
 		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",
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+			   card->contrnr,
 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 			       cmsg->Info, capi_info2str(cmsg->Info), 
 			       cmsg->adr.adrNCCI);
@@ -1127,6 +1252,9 @@
 		break;
 
 	case CAPI_RESET_B3_IND:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+		ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND);
 		capi_cmsg_answer(cmsg);
 		send_message(card, cmsg);
 		break;
@@ -1140,18 +1268,21 @@
 		goto ignored;
 
 	default:
-		printk(KERN_ERR "capidrv: got %s for ncci 0x%x ???",
+		printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrNCCI);
 	}
 	return;
       ignored:
-	printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n",
+	printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrNCCI);
 	return;
       notfound:
-	printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+	printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+	       card->contrnr,
 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 	       cmsg->adr.adrNCCI);
 }
@@ -1169,7 +1300,8 @@
 		return;
 	}
 	if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) {
-		printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+		printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+		       card->contrnr,
 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
 		       cmsg->adr.adrNCCI);
 		kfree_skb(skb, FREE_READ);
@@ -1190,7 +1322,8 @@
 	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));
+			printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n",
+					applid, capi_cmsg2str(&s_cmsg));
 
 		if (s_cmsg.Command == CAPI_DATA_B3
 		    && s_cmsg.Subcommand == CAPI_IND) {
@@ -1209,23 +1342,171 @@
 
 /* ------------------------------------------------------------------- */
 
+#define PUTBYTE_TO_STATUS(card, byte) \
+	do { \
+		*(card)->q931_write++ = (byte); \
+        	if ((card)->q931_write > (card)->q931_end) \
+	  		(card)->q931_write = (card)->q931_buf; \
+	} while (0)
+
+static void handle_dtrace_data(capidrv_contr *card,
+			     int send, int level2, __u8 *data, __u16 len)
+{
+    long flags;
+    __u8 *p, *end;
+    isdn_ctrl cmd;
+
+    if (!len) {
+       printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
+				card->contrnr, len);
+       return;
+    }
+
+    save_flags(flags);
+    cli();
+
+    if (level2) {
+        PUTBYTE_TO_STATUS(card, 'D');
+        PUTBYTE_TO_STATUS(card, '2');
+        PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        PUTBYTE_TO_STATUS(card, ':');
+    } else {
+        PUTBYTE_TO_STATUS(card, 'D');
+        PUTBYTE_TO_STATUS(card, '3');
+        PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        PUTBYTE_TO_STATUS(card, ':');
+    }
+
+    for (p = data, end = data+len; p < end; p++) {
+       __u8 w;
+       PUTBYTE_TO_STATUS(card, ' ');
+       w = (*p >> 4) & 0xf;
+       PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+       w = *p & 0xf;
+       PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+    }
+    PUTBYTE_TO_STATUS(card, '\n');
+
+    restore_flags(flags);
+    
+    cmd.command = ISDN_STAT_STAVAIL;
+    cmd.driver = card->myid;
+    cmd.arg = len*3+5;
+    card->interface.statcallb(&cmd);
+}
+
+/* ------------------------------------------------------------------- */
+
 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);
+		printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n",
+				card->contrnr, c->arg);
 		return -EINVAL;
 	}
 	return -EINVAL;
 }
 
+/*
+ * Handle leased lines (CAPI-Bundling)
+ */
+
+struct internal_bchannelinfo {
+   unsigned short channelalloc;
+   unsigned short operation;
+   unsigned char  cmask[31];
+};
+
+static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep)
+{
+	unsigned long bmask = 0;
+	int active = !0;
+	char *s;
+	int i;
+
+	if (strncmp(teln, "FV:", 3) != 0)
+		return 1;
+	s = teln + 3;
+	while (*s && *s == ' ') s++;
+	if (!*s) return -2;
+	if (*s == 'p' || *s == 'P') {
+		active = 0;
+		s++;
+	}
+	if (*s == 'a' || *s == 'A') {
+		active = !0;
+		s++;
+	}
+	while (*s) {
+		int digit1 = 0;
+		int digit2 = 0;
+		if (!isdigit(*s)) return -3;
+		while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; }
+		if (digit1 <= 0 && digit1 > 30) return -4;
+		if (*s == 0 || *s == ',' || *s == ' ') {
+			bmask |= (1 << digit1);
+			digit1 = 0;
+			if (*s) s++;
+			continue;
+		}
+		if (*s != '-') return -5;
+		s++;
+		if (!isdigit(*s)) return -3;
+		while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; }
+		if (digit2 <= 0 && digit2 > 30) return -4;
+		if (*s == 0 || *s == ',' || *s == ' ') {
+			if (digit1 > digit2)
+				for (i = digit2; i <= digit1 ; i++)
+					bmask |= (1 << i);
+			else 
+				for (i = digit1; i <= digit2 ; i++)
+					bmask |= (1 << i);
+			digit1 = digit2 = 0;
+			if (*s) s++;
+			continue;
+		}
+		return -6;
+	}
+	if (activep) *activep = active;
+	if (bmaskp) *bmaskp = bmask;
+	return 0;
+}
+
+static int FVteln2capi20(char *teln, __u8 AdditionalInfo[1+2+2+31])
+{
+	unsigned long bmask;
+	int active;
+	int rc, i;
+   
+	rc = decodeFVteln(teln, &bmask, &active);
+	if (rc) return rc;
+	/* Length */
+	AdditionalInfo[0] = 2+2+31;
+        /* Channel: 3 => use channel allocation */
+        AdditionalInfo[1] = 3; AdditionalInfo[2] = 0;
+	/* Operation: 0 => DTE mode, 1 => DCE mode */
+        if (active) {
+   		AdditionalInfo[3] = 0; AdditionalInfo[4] = 0;
+   	} else {
+   		AdditionalInfo[3] = 1; AdditionalInfo[4] = 0;
+	}
+	/* Channel mask array */
+	AdditionalInfo[5] = 0; /* no D-Channel */
+	for (i=1; i <= 30; i++)
+		AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0;
+	return 0;
+}
+
 static int capidrv_command(isdn_ctrl * c, capidrv_contr * card)
 {
 	isdn_ctrl cmd;
 	struct capidrv_bchan *bchan;
 	struct capidrv_plci *plcip;
+	__u8 AdditionalInfo[1+2+2+31];
+        int rc, isleasedline = 0;
 
 	if (c->command == ISDN_CMD_IOCTL)
 		return capidrv_ioctl(c, card);
@@ -1236,7 +1517,8 @@
 			__u8 called[ISDN_MSNLEN + 2];
 
 			if (debugmode)
-				printk(KERN_DEBUG "capidrv: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+				printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+					card->contrnr,
 					c->arg,
 				        c->parm.setup.phone,
 				        c->parm.setup.si1,
@@ -1246,7 +1528,8 @@
 			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",
+				printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+					card->contrnr,
 			        	c->arg, 
 				        c->parm.setup.phone,
 				        c->parm.setup.si1,
@@ -1261,14 +1544,26 @@
 			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);
+                        rc = FVteln2capi20(bchan->num, AdditionalInfo);
+			isleasedline = (rc == 0);
+			if (rc < 0)
+				printk(KERN_ERR "capidrv-%d: WARNING: illegal leased linedefinition \"%s\"\n", card->contrnr, bchan->num);
+
+			if (isleasedline) {
+				calling[0] = 0;
+				called[0] = 0;
+			        if (debugmode)
+					printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr);
+			} else {
+		        	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,
@@ -1288,7 +1583,8 @@
 					      0,	/* BC */
 					      0,	/* LLC */
 					      0,	/* HLC */
-					      0,	/* BChannelinformation */
+					      /* BChannelinformation */
+					      isleasedline ? AdditionalInfo : 0,
 					      0,	/* Keypadfacility */
 					      0,	/* Useruserdata */
 					      0		/* Facilitydataarray */
@@ -1301,6 +1597,7 @@
 				return -1;
 			}
 			plcip->msgid = cmdcmsg.Messagenumber;
+			plcip->leasedline = isleasedline;
 			plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ);
 			send_message(card, &cmdcmsg);
 			return 0;
@@ -1308,10 +1605,11 @@
 
 	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];
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n",
+			       card->contrnr,
+			       c->arg, bchan->l2, bchan->l3);
 
 		capi_fill_CONNECT_RESP(&cmdcmsg,
 				       global.appid,
@@ -1339,19 +1637,22 @@
 
 	case ISDN_CMD_ACCEPTB:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+			       card->contrnr,
 			       c->arg);
 		return -ENOSYS;
 
 	case ISDN_CMD_HANGUP:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n",
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n",
+			       card->contrnr,
 			       c->arg);
 		bchan = &card->bchans[c->arg % card->nbchan];
 
 		if (bchan->disconnecting) {
 			if (debugmode)
-				printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n",
+				printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n",
+				       card->contrnr,
 				       c->arg);
 			return 0;
 		}
@@ -1390,23 +1691,26 @@
 
 	case ISDN_CMD_SETL2:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n",
+			printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n",
+			       card->contrnr,
 			       (c->arg & 0xff), (c->arg >> 8));
-		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan = &card->bchans[(c->arg & 0xff) % 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",
+			printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n",
+			       card->contrnr,
 			       (c->arg & 0xff), (c->arg >> 8));
-		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan = &card->bchans[(c->arg & 0xff) % 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",
+			printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n",
+			       card->contrnr,
 			       c->parm.num, c->arg);
 		bchan = &card->bchans[c->arg % card->nbchan];
 		strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN);
@@ -1414,46 +1718,54 @@
 
 	case ISDN_CMD_CLREAZ:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: clearing EAZ on chan %ld\n", c->arg);
+			printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n",
+					card->contrnr, 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);
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_LOCK (%ld)\n", card->contrnr, 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);
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_UNLOCK (%ld)\n",
+					card->contrnr, c->arg);
 		MOD_DEC_USE_COUNT;
 		break;
 
 /* never called */
 	case ISDN_CMD_GETL2:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL2\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_GETL3:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL3\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_GETEAZ:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETEAZ\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_SETSIL:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_SETSIL\n",
+					card->contrnr);
 		return -ENODEV;
 	case ISDN_CMD_GETSIL:
 		if (debugmode)
-			printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n");
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETSIL\n",
+					card->contrnr);
 		return -ENODEV;
 	default:
-		printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", c->command);
+		printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n",
+					card->contrnr, c->command);
 		return -EINVAL;
 	}
 	return 0;
@@ -1467,8 +1779,8 @@
 		return capidrv_command(c, card);
 
 	printk(KERN_ERR
-	     "capidrv: if_command %d called with invalid driverId %d!\n",
-	       c->command, c->driver);
+	     "capidrv-%d: if_command %d called with invalid driverId %d!\n",
+	       card->contrnr, c->command, c->driver);
 	return -ENODEV;
 }
 
@@ -1484,15 +1796,15 @@
 	__u16 errcode;
 
 	if (!card) {
-		printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
-		       id);
+		printk(KERN_ERR "capidrv-%d: if_sendbuf called with invalid driverId %d!\n",
+		       card->contrnr, 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);
+		printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n",
+		       card->contrnr, card->name, channel);
 		return 0;
 	}
 	capi_fill_DATA_B3_REQ(&sendcmsg, global.appid, card->msgid++,
@@ -1507,12 +1819,13 @@
 	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");
+			printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n",
+					card->contrnr);
 			return 0;
 		}
 #if 0
-		printk(KERN_DEBUG "capidrv: only %d bytes headroom\n",
-		       skb_headroom(skb));
+		printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom\n",
+		       card->contrnr, skb_headroom(skb));
 #endif
                 SET_SKB_FREE(nskb);
 		memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen);
@@ -1542,6 +1855,82 @@
 	}
 }
 
+static int if_readstat(__u8 *buf, int len, int user, int id, int channel)
+{
+	capidrv_contr *card = findcontrbydriverid(id);
+	int count;
+	__u8 *p;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv-%d: if_readstat called with invalid driverId %d!\n",
+		       card->contrnr, id);
+		return -ENODEV;
+	}
+
+	for (p=buf, count=0; count < len; p++, count++) {
+	        if (user)
+	                put_user(*card->q931_read++, p);
+	        else
+	                *p = *card->q931_read++;
+	        if (card->q931_read > card->q931_end)
+	                card->q931_read = card->q931_buf;
+	}
+	return count;
+
+}
+
+static void enable_dchannel_trace(capidrv_contr *card)
+{
+        __u8 manufacturer[CAPI_MANUFACTURER_LEN];
+        capi_version version;
+	__u16 contr = card->contrnr;
+	__u16 errcode;
+	__u16 avmversion[3];
+
+        errcode = (*capifuncs->capi_get_manufacturer)(contr, manufacturer);
+        if (errcode != CAPI_NOERROR) {
+	   printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n",
+			card->name, errcode);
+	   return;
+	}
+	if (strstr(manufacturer, "AVM") == 0) {
+	   printk(KERN_ERR "%s: not from AVM, no d-channel trace possible\n",
+			card->name);
+	   return;
+	}
+        errcode = (*capifuncs->capi_get_version)(contr, &version);
+        if (errcode != CAPI_NOERROR) {
+	   printk(KERN_ERR "%s: can't get version (0x%x)\n",
+			card->name, errcode);
+	   return;
+	}
+	avmversion[0] = (version.majormanuversion >> 4) & 0x0f;
+	avmversion[1] = (version.majormanuversion << 4) & 0xf0;
+	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
+	avmversion[2] |= version.minormanuversion & 0x0f;
+
+        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+		printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
+		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
+					   card->msgid++,
+					   contr,
+					   0x214D5641,  /* ManuID */
+					   0,           /* Class */
+					   1,           /* Function */
+					   (_cstruct)"\004\200\014\000\000");
+	} else {
+		printk(KERN_INFO "%s: D3 trace enabled\n", card->name);
+		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
+					   card->msgid++,
+					   contr,
+					   0x214D5641,  /* ManuID */
+					   0,           /* Class */
+					   1,           /* Function */
+					   (_cstruct)"\004\002\003\000\000");
+	}
+	send_message(card, &cmdcmsg);
+}
+
 static int capidrv_addcontr(__u16 contr, struct capi_profile *profp)
 {
 	capidrv_contr *card;
@@ -1571,7 +1960,7 @@
 	card->interface.command = if_command;
 	card->interface.writebuf_skb = if_sendbuf;
 	card->interface.writecmd = 0;
-	card->interface.readstat = 0;
+	card->interface.readstat = if_readstat;
 	card->interface.features = ISDN_FEATURE_L2_X75I |
 	    ISDN_FEATURE_L2_X75UI |
 	    ISDN_FEATURE_L2_X75BUI |
@@ -1584,6 +1973,9 @@
 	card->next = global.contr_list;
 	global.contr_list = card;
 	global.ncontr++;
+	card->q931_read = card->q931_buf;
+	card->q931_write = card->q931_buf;
+	card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1;
 
 	if (!register_isdn(&card->interface)) {
 		global.contr_list = global.contr_list->next;
@@ -1603,7 +1995,7 @@
 	cmd.command = ISDN_STAT_RUN;
 	card->interface.statcallb(&cmd);
 
-	card->cipmask = 1;	/* any */
+	card->cipmask = 0x1FFF03FF;	/* any */
 	card->cipmask2 = 0;
 
 	capi_fill_LISTEN_REQ(&cmdcmsg, global.appid,
@@ -1619,6 +2011,9 @@
 	printk(KERN_INFO "%s: now up (%d B channels)\n",
 		card->name, card->nbchan);
 
+        if (card->nbchan == 2)  /* no T1 */
+        	enable_dchannel_trace(card);
+
 	return 0;
 }
 
@@ -1709,8 +2104,8 @@
 	} else
 		strcpy(rev, " ??? ");
 
-	rparam.level3cnt = 2;
-	rparam.datablkcnt = 8;
+	rparam.level3cnt = -2;  /* number of bchannels twice */
+	rparam.datablkcnt = 16;
 	rparam.datablklen = 2048;
 	errcode = (*capifuncs->capi_register) (&rparam, &global.appid);
 	if (errcode) {
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/capidrv.h linux/drivers/isdn/avmb1/capidrv.h
--- v2.0.35/linux/drivers/isdn/avmb1/capidrv.h	Mon Aug  4 17:33:59 1997
+++ linux/drivers/isdn/avmb1/capidrv.h	Sun Nov 15 10:32:57 1998
@@ -1,11 +1,19 @@
 /*
- * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 calle Exp $
+ * $Id: capidrv.h,v 1.1.2.1 1998/03/20 14:38:28 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.2.1  1998/03/20 14:38:28  calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
  * Revision 1.1  1997/03/04 21:50:33  calle
  * Frirst version in isdn4linux
  *
@@ -49,34 +57,70 @@
 #define ST_PLCI_ACCEPTING		6	/* P-4 */
 #define ST_PLCI_DISCONNECTING		7	/* P-5 */
 #define ST_PLCI_DISCONNECTED		8	/* P-6 */
+#define ST_PLCI_RESUMEING		9	/* P-0.Res */
+#define ST_PLCI_RESUME			10	/* P-Res */
+#define ST_PLCI_HELD			11	/* P-HELD */
 
-#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_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 */
+						   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 */
+						   P-ACT -> P-5
+						   P-Res -> P-5 (*)
+						   P-HELD -> 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 */
+						   P-ACT -> P-6
+						   P-Res -> P-6 (*)
+						   P-HELD -> 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 */
+						   P-4 -> P-5
+						   */
+#define EV_PLCI_DISCONNECT_RESP		11	/* P-6 -> P-0
+                                                   */
+#define EV_PLCI_CONNECT_RESP		12	/* P-6 -> P-0
+                                                   */
+
+#define EV_PLCI_RESUME_REQ		13	/* P-0 -> P-0.Res
+                                                 */
+#define EV_PLCI_RESUME_CONF_OK		14	/* P-0.Res -> P-Res
+                                                 */
+#define EV_PLCI_RESUME_CONF_ERROR	15	/* P-0.Res -> P-0
+                                                 */
+#define EV_PLCI_RESUME_IND		16	/* P-Res -> P-ACT
+                                                 */
+#define EV_PLCI_HOLD_IND		17	/* P-ACT -> P-HELD
+                                                 */
+#define EV_PLCI_RETRIEVE_IND		18	/* P-HELD -> P-ACT
+                                                 */
+#define EV_PLCI_SUSPEND_IND		19	/* P-ACT -> P-5
+                                                 */
+#define EV_PLCI_CD_IND			20	/* P-2 -> P-5
+                                                 */
 
 /*
  * per ncci state machine
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/capiutil.c linux/drivers/isdn/avmb1/capiutil.c
--- v2.0.35/linux/drivers/isdn/avmb1/capiutil.c	Mon Jul 13 13:46:28 1998
+++ linux/drivers/isdn/avmb1/capiutil.c	Sun Nov 15 10:32:57 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $
+ * $Id: capiutil.c,v 1.3.2.1 1998/08/03 15:52:21 paul Exp $
  *
  * CAPI 2.0 convert capi message to capi message struct
  *
@@ -7,6 +7,10 @@
  * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de)
  *
  * $Log: capiutil.c,v $
+ * Revision 1.3.2.1  1998/08/03 15:52:21  paul
+ * various changes from 2.0.3[45] kernel sources, as suggested by
+ * Oliver.Lauer@coburg.baynet.de
+ *
  * Revision 1.3  1997/05/18 09:24:18  calle
  * added verbose disconnect reason reporting to avmb1.
  * some fixes in capi20 interface.
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/avmb1/compat.h linux/drivers/isdn/avmb1/compat.h
--- v2.0.35/linux/drivers/isdn/avmb1/compat.h	Wed Oct 15 15:25:22 1997
+++ linux/drivers/isdn/avmb1/compat.h	Sun Nov 15 10:32:57 1998
@@ -1,11 +1,14 @@
 /*
- * $Id: compat.h,v 1.1 1997/03/04 21:50:36 calle Exp $
+ * $Id: compat.h,v 1.1.2.1 1998/10/25 14:36:22 fritz 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.2.1  1998/10/25 14:36:22  fritz
+ * Backported from MIPS (Cobalt).
+ *
  * Revision 1.1  1997/03/04 21:50:36  calle
  * Frirst version in isdn4linux
  *
@@ -21,6 +24,7 @@
 #define __COMPAT_H__
 
 #include <linux/version.h>
+#include <asm/segment.h>
 #include <linux/isdnif.h>
 
 #if LINUX_VERSION_CODE >= 0x020112	/* 2.1.18 */
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- v2.0.35/linux/drivers/isdn/hisax/Makefile	Mon Aug  4 17:33:59 1997
+++ linux/drivers/isdn/hisax/Makefile	Sun Nov 15 10:32:57 1998
@@ -1,7 +1,14 @@
 L_OBJS :=
 M_OBJS :=
-O_OBJS := isdnl1.o config.o tei.o isdnl2.o isdnl3.o \
-          q931.o callc.o fsm.o
+LX_OBJS :=
+MX_OBJS :=
+O_OBJS :=
+OX_OBJS :=
+L_TARGET :=
+O_TARGET :=
+
+O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \
+          lmgr.o q931.o callc.o fsm.o cert.o
 
 # EXTRA_CFLAGS += -S
 
@@ -17,31 +24,127 @@
 	 O_OBJS += l3_1tr6.o
 endif
 
+ISAC_OBJ :=
+ARCOFI_OBJ :=
+HSCX_OBJ :=
+ISAR_OBJ :=
+HFC_OBJ :=
+HFC_2BDS0 :=
+
 ifeq ($(CONFIG_HISAX_16_0),y)
 	 O_OBJS += teles0.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
 endif
 
 ifeq ($(CONFIG_HISAX_16_3),y)
 	 O_OBJS += teles3.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_TELESPCI),y)
+	 O_OBJS += telespci.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_S0BOX),y)
+	 O_OBJS += s0box.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
 endif
 
 ifeq ($(CONFIG_HISAX_AVM_A1),y)
 	 O_OBJS += avm_a1.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
 endif
 
-ifeq ($(CONFIG_HISAX_ELSA_PCC),y)
-	 O_OBJS += elsa.o
+ifeq ($(CONFIG_HISAX_AVM_A1_PCMCIA),y)
+	 O_OBJS += avm_a1p.o
+ 	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+ 
+ifeq ($(CONFIG_HISAX_FRITZPCI),y)
+	 O_OBJS += avm_pci.o
+ 	 ISAC_OBJ := isac.o
 endif
 
-ifeq ($(CONFIG_HISAX_ELSA_PCMCIA),y)
+
+ifeq ($(CONFIG_HISAX_ELSA),y)
 	 O_OBJS += elsa.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+	 ARCOFI_OBJ := arcofi.o
 endif
 
 ifeq ($(CONFIG_HISAX_IX1MICROR2),y)
 	 O_OBJS += ix1_micro.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_DIEHLDIVA),y)
+	 O_OBJS += diva.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_ASUSCOM),y)
+	 O_OBJS += asuscom.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_TELEINT),y)
+	 O_OBJS += teleint.o
+	 ISAC_OBJ := isac.o
+	 HFC_OBJ := hfc_2bs0.o
+endif
+
+ifeq ($(CONFIG_HISAX_SEDLBAUER),y)
+	 O_OBJS += sedlbauer.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+	 ISAR_OBJ := isar.o
 endif
 
+ifeq ($(CONFIG_HISAX_SPORTSTER),y)
+	 O_OBJS += sportster.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_MIC),y)
+	 O_OBJS += mic.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_NETJET),y)
+	 O_OBJS += netjet.o
+	 ISAC_OBJ := isac.o
+endif
+
+ifeq ($(CONFIG_HISAX_TELES3C),y)
+	 O_OBJS += teles3c.o
+	 HFC_2BDS0 := hfc_2bds0.o
+endif
+
+ifeq ($(CONFIG_HISAX_NICCY),y)
+	 O_OBJS += niccy.o
+	 ISAC_OBJ := isac.o
+	 HSCX_OBJ := hscx.o
+endif
+
+O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(ARCOFI_OBJ) 
+O_OBJS += $(HFC_OBJ) $(HFC_2BDS0)
+OX_OBJS += config.o
+
 O_TARGET :=
+
 ifeq ($(CONFIG_ISDN_DRV_HISAX),y)
   O_TARGET += hisax.o
 else
@@ -51,4 +154,14 @@
   endif
 endif
 
+
 include $(TOPDIR)/Rules.make
+
+MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \
+	    tei.c callc.c cert.c l3dss1.c l3_1tr6.c elsa.c
+
+CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
+
+cert.o:	$(MD5FILES) md5sums.asc
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -D CERTIFICATION=$(CERT) -c -o cert.o cert.c
+
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/arcofi.c linux/drivers/isdn/hisax/arcofi.c
--- v2.0.35/linux/drivers/isdn/hisax/arcofi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/arcofi.c	Sun Nov 15 10:32:57 1998
@@ -0,0 +1,76 @@
+/* $Id: arcofi.c,v 1.1.2.5 1998/09/30 22:20:03 keil Exp $
+
+ * arcofi.c   Ansteuerung ARCOFI 2165
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ *
+ * $Log: arcofi.c,v $
+ * Revision 1.1.2.5  1998/09/30 22:20:03  keil
+ * Cosmetics
+ *
+ * Revision 1.1.2.4  1998/09/27 13:05:29  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.1.2.3  1998/05/27 18:04:48  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.2  1998/04/11 18:45:13  keil
+ * New interface
+ *
+ * Revision 1.1.2.1  1997/11/15 18:57:37  keil
+ * ARCOFI 2165 support
+ *
+ *
+ */
+ 
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+
+int
+send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive) {
+	u_char val;
+	long flags;
+	int cnt=30;
+	
+	cs->mon_txp = 0;
+	cs->mon_txc = msg[0];
+	memcpy(cs->mon_tx, &msg[1], cs->mon_txc);
+	switch(bc) {
+		case 0: break;
+		case 1: cs->mon_tx[1] |= 0x40;
+			break;
+		default: break;
+	}
+	cs->mocr &= 0x0f;
+	cs->mocr |= 0xa0;
+	test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags);
+	if (receive)
+		test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags);
+	cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+	val = cs->readisac(cs, ISAC_MOSR);
+	cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]);
+	cs->mocr |= 0x10;
+	cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+	save_flags(flags);
+	sti();
+	while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
+		cnt--;
+		udelay(500);
+	}
+	if (receive) {
+		while (cnt && !test_bit(HW_MON1_RX_END, &cs->HW_Flags)) {
+			cnt--;
+			udelay(500);
+		}
+	}
+	restore_flags(flags);
+	if (cnt <= 0) {
+		printk(KERN_WARNING"HiSax arcofi monitor timed out\n");
+		debugl1(cs, "HiSax arcofi monitor timed out");
+	}
+	return(cnt);	
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/arcofi.h linux/drivers/isdn/hisax/arcofi.h
--- v2.0.35/linux/drivers/isdn/hisax/arcofi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/arcofi.h	Sun Nov 15 10:32:57 1998
@@ -0,0 +1,23 @@
+/* $Id: arcofi.h,v 1.1.2.3 1998/05/27 18:04:50 keil Exp $
+
+ * arcofi.h   Ansteuerung ARCOFI 2165
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: arcofi.h,v $
+ * Revision 1.1.2.3  1998/05/27 18:04:50  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.2  1998/04/11 18:45:14  keil
+ * New interface
+ *
+ * Revision 1.1.2.1  1997/11/15 18:57:38  keil
+ * ARCOFI 2165 support
+ *
+ *
+ */
+ 
+#define ARCOFI_USE	1
+
+extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/asuscom.c linux/drivers/isdn/hisax/asuscom.c
--- v2.0.35/linux/drivers/isdn/hisax/asuscom.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/asuscom.c	Sun Nov 15 10:32:57 1998
@@ -0,0 +1,407 @@
+/* $Id: asuscom.c,v 1.1.2.4 1998/11/03 00:05:42 keil Exp $
+
+ * asuscom.c     low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
+ *
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ * Thanks to  ASUSCOM NETWORK INC. Taiwan and  Dynalink NL for informations
+ *
+ *
+ * $Log: asuscom.c,v $
+ * Revision 1.1.2.4  1998/11/03 00:05:42  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.3  1998/06/18 23:10:26  keil
+ * Support for new IPAC card
+ *
+ * Revision 1.1.2.2  1998/04/08 21:58:37  keil
+ * New init code
+ *
+ * Revision 1.1.2.1  1998/01/27 22:34:02  keil
+ * dynalink ----> asuscom
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *Asuscom_revision = "$Revision: 1.1.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ASUS_ISAC	0
+#define ASUS_HSCX	1
+#define ASUS_ADR	2
+#define ASUS_CTRL_U7	3
+#define ASUS_CTRL_POTS	5
+
+#define ASUS_IPAC_ALE	0
+#define ASUS_IPAC_DATA	1
+
+#define ASUS_ISACHSCX	1
+#define ASUS_IPAC	2
+
+/* CARD_ADR (Write) */
+#define ASUS_RESET      0x80	/* Bit 7 Reset-Leitung */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr,
+			cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr,
+		 cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 20;
+
+	if (!cs) {
+		printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n");
+		return;
+	}
+	ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ASUS IRQ LOOP\n");
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0);
+}
+
+void
+release_io_asuscom(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.asus.cfg_reg)
+		release_region(cs->hw.asus.cfg_reg, bytecnt);
+}
+
+static void
+reset_asuscom(struct IsdnCardState *cs)
+{
+	long flags;
+
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20);
+	else
+		byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
+	else
+		byteout(cs->hw.asus.adr, 0);	/* Reset Off */
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	if (cs->subtyp == ASUS_IPAC) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12);
+	}
+	restore_flags(flags);
+}
+
+static int
+Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_asuscom(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_asuscom(cs);
+			return(0);
+		case CARD_SETIRQ:
+			if (cs->subtyp == ASUS_IPAC)
+				return(request_irq(cs->irq, &asuscom_interrupt_ipac,
+					I4L_IRQ_FLAG, "HiSax", cs));
+			else
+				return(request_irq(cs->irq, &asuscom_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			cs->debug |= L1_DEB_IPAC;
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_asuscom(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	u_char val;
+	char tmp[64];
+
+	strcpy(tmp, Asuscom_revision);
+	printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_ASUSCOM)
+		return (0);
+
+	bytecnt = 8;
+	cs->hw.asus.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (check_region((cs->hw.asus.cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.asus.cfg_reg,
+		       cs->hw.asus.cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn");
+	}
+	printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
+		cs->hw.asus.cfg_reg, cs->irq);
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Asus_card_msg;
+	val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE, 
+		cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
+	if (val == 1) {
+		cs->subtyp = ASUS_IPAC;
+		cs->hw.asus.adr  = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		printk(KERN_INFO "Asus: IPAC version %x\n", val);
+	} else {
+		cs->subtyp = ASUS_ISACHSCX;
+		cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
+		cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
+		cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		ISACVersion(cs, "ISDNLink:");
+		if (HscxVersion(cs, "ISDNLink:")) {
+			printk(KERN_WARNING
+		     	"ISDNLink: wrong HSCX versions check IO address\n");
+			release_io_asuscom(cs);
+			return (0);
+		}
+	}
+	printk(KERN_INFO "ISDNLink: resetting card\n");
+	reset_asuscom(cs);
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/avm_a1.c linux/drivers/isdn/hisax/avm_a1.c
--- v2.0.35/linux/drivers/isdn/hisax/avm_a1.c	Tue Aug  5 09:00:12 1997
+++ linux/drivers/isdn/hisax/avm_a1.c	Sun Nov 15 10:32:57 1998
@@ -1,11 +1,45 @@
-/* $Id: avm_a1.c,v 1.6 1997/04/13 19:54:07 keil Exp $
+/* $Id: avm_a1.c,v 1.6.2.12 1998/11/03 00:05:44 keil Exp $
 
  * avm_a1.c     low level stuff for AVM A1 (Fritz) isdn cards
  *
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *
  *
  * $Log: avm_a1.c,v $
+ * Revision 1.6.2.12  1998/11/03 00:05:44  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.6.2.11  1998/09/27 13:05:30  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.6.2.10  1998/05/27 18:04:50  keil
+ * HiSax 3.0
+ *
+ * Revision 1.6.2.9  1998/04/08 21:58:39  keil
+ * New init code
+ *
+ * Revision 1.6.2.8  1998/01/27 22:37:49  keil
+ * fast io
+ *
+ * Revision 1.6.2.7  1998/01/13 23:06:11  keil
+ * really disable internal timer
+ *
+ * Revision 1.6.2.6  1998/01/02 06:49:01  calle
+ * Perodic timer of A1 now disabled, no need for linux driver.
+ *
+ * Revision 1.6.2.5  1997/11/15 18:50:41  keil
+ * new common init function
+ *
+ * Revision 1.6.2.4  1997/10/17 22:13:29  keil
+ * update to last hisax version
+ *
+ * Revision 2.1  1997/07/27 21:47:13  keil
+ * new interface structures
+ *
+ * Revision 2.0  1997/06/26 11:02:48  keil
+ * New Layer and card interface
+ *
  * Revision 1.6  1997/04/13 19:54:07  keil
  * Change in IRQ check delay for SMP
  *
@@ -27,17 +61,20 @@
  *
  */
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "avm_a1.h"
+#include "isac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
 
 extern const char *CardType[];
-const char *avm_revision = "$Revision: 1.6 $";
+static const char *avm_revision = "$Revision: 1.6.2.12 $";
+
+#define	 AVM_A1_STAT_ISAC	0x01
+#define	 AVM_A1_STAT_HSCX	0x02
+#define	 AVM_A1_STAT_TIMER	0x04
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 static inline u_char
 readreg(unsigned int adr, u_char off)
@@ -55,906 +92,299 @@
 static inline void
 read_fifo(unsigned int adr, u_char * data, int size)
 {
-	insb(adr - 0x400, data, size);
+	insb(adr, data, size);
 }
 
 static void
 write_fifo(unsigned int adr, u_char * data, int size)
 {
-	outsb(adr - 0x400, data, size);
+	outsb(adr, 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");
-}
+/* Interface functions */
 
-static inline void
-writehscxCMDR(int adr, u_char data)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC(adr);
-	writereg(adr, HSCX_CMDR, data);
-	restore_flags(flags);
+	return (readreg(cs->hw.avm.isac, offset));
 }
 
-/*
- * fast interrupt here
- */
-
 static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	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);
+	writereg(cs->hw.avm.isac, offset, value);
 }
 
-/*
- * HSCX stuff goes here
- */
-
 static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	read_fifo(cs->hw.avm.isacfifo, data, size);
 }
 
 static void
-hscx_fill_fifo(struct HscxState *hsp)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	write_fifo(cs->hw.avm.isacfifo, data, size);
 }
 
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	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);
-	}
+	return (readreg(cs->hw.avm.hscx[hscx], offset));
 }
 
-/*
- * ISAC stuff goes here
- */
-
 static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	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);
-	}
+	writereg(cs->hw.avm.hscx[hscx], offset, value);
 }
 
-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);
-		}
-	}
-}
+/*
+ * fast interrupt HSCX stuff goes here
+ */
 
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
+#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
 
-	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);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, sval, stat = 0;
-	char tmp[32];
 
-	sp = (struct IsdnCardState *) irq2dev_map[intno];
-
-	if (!sp) {
+	if (!cs) {
 		printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
 		return;
 	}
-	while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) {
+	while (((sval = bytein(cs->hw.avm.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);
-		}
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			sval = bytein(cs->hw.avm.cfg_reg);
+		} else if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
 		if (!(sval & AVM_A1_STAT_HSCX)) {
-			val = readreg(sp->hscx[1], HSCX_ISTA);
+			val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
 			if (val) {
-				hscx_int_main(sp, val);
+				hscx_int_main(cs, val);
 				stat |= 1;
 			}
 		}
 		if (!(sval & AVM_A1_STAT_ISAC)) {
-			val = readreg(sp->isac, ISAC_ISTA);
+			val = readreg(cs->hw.avm.isac, ISAC_ISTA);
 			if (val) {
-				isac_interrupt(sp, val);
+				isac_interrupt(cs, 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);
+		writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF);
+		writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF);
+		writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0);
+		writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0);
 	}
 	if (stat & 2) {
-		writereg(sp->isac, ISAC_MASK, 0xFF);
-		writereg(sp->isac, ISAC_MASK, 0x0);
+		writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.avm.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_ioregs(struct IsdnCardState *cs, int mask)
 {
-	release_region(card->sp->cfg_reg, 8);
+	release_region(cs->hw.avm.cfg_reg, 8);
 	if (mask & 1)
-		release_region(card->sp->isac, 32);
+		release_region(cs->hw.avm.isac + 32, 32);
 	if (mask & 2)
-		release_region(card->sp->isac - 0x400, 1);
+		release_region(cs->hw.avm.isacfifo, 1);
 	if (mask & 4)
-		release_region(card->sp->hscx[0], 32);
+		release_region(cs->hw.avm.hscx[0] + 32, 32);
 	if (mask & 8)
-		release_region(card->sp->hscx[0] - 0x400, 1);
+		release_region(cs->hw.avm.hscxfifo[0], 1);
 	if (mask & 0x10)
-		release_region(card->sp->hscx[1], 32);
+		release_region(cs->hw.avm.hscx[1] + 32, 32);
 	if (mask & 0x20)
-		release_region(card->sp->hscx[1] - 0x400, 1);
+		release_region(cs->hw.avm.hscxfifo[1], 1);
 }
 
-void
-release_io_avm_a1(struct IsdnCard *card)
-{
-	release_ioregs(card, 0x3f);
-}
-
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	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);
-		}
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_ioregs(cs, 0x3f);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &avm_a1_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 1);
+			byteout(cs->hw.avm.cfg_reg, 0x16);
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			inithscxisac(cs, 2);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_avm_a1(struct IsdnCard *card)
+__initfunc(int
+setup_avm_a1(struct IsdnCard *card))
 {
-	u_char val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	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)
+	printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->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)) {
+	cs->hw.avm.cfg_reg = card->para[1] + 0x1800;
+	cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20;
+	cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20;
+	cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20;
+	cs->hw.avm.isacfifo = card->para[1] + 0x1000;
+	cs->hw.avm.hscxfifo[0] = card->para[1];
+	cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800;
+	cs->irq = card->para[0];
+	if (check_region((cs->hw.avm.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);
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 8);
 		return (0);
 	} else {
-		request_region(sp->cfg_reg, 8, "avm cfg");
+		request_region(cs->hw.avm.cfg_reg, 8, "avm cfg");
 	}
-	if (check_region((sp->isac), 32)) {
+	if (check_region((cs->hw.avm.isac + 32), 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);
+		       CardType[cs->typ],
+		       cs->hw.avm.isac + 32,
+		       cs->hw.avm.isac + 64);
+		release_ioregs(cs, 0);
 		return (0);
 	} else {
-		request_region(sp->isac, 32, "HiSax isac");
+		request_region(cs->hw.avm.isac + 32, 32, "HiSax isac");
 	}
-	if (check_region((sp->isac - 0x400), 1)) {
+	if (check_region((cs->hw.avm.isacfifo), 1)) {
 		printk(KERN_WARNING
 		       "HiSax: %s isac fifo port %x already in use\n",
-		       CardType[sp->typ],
-		       sp->isac - 0x400);
-		release_ioregs(card, 1);
+		       CardType[cs->typ],
+		       cs->hw.avm.isacfifo);
+		release_ioregs(cs, 1);
 		return (0);
 	} else {
-		request_region(sp->isac - 0x400, 1, "HiSax isac fifo");
+		request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo");
 	}
-	if (check_region((sp->hscx[0]), 32)) {
+	if (check_region((cs->hw.avm.hscx[0]) + 32, 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);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[0] + 32,
+		       cs->hw.avm.hscx[0] + 64);
+		release_ioregs(cs, 3);
 		return (0);
 	} else {
-		request_region(sp->hscx[0], 32, "HiSax hscx A");
+		request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A");
 	}
-	if (check_region((sp->hscx[0] - 0x400), 1)) {
+	if (check_region(cs->hw.avm.hscxfifo[0], 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);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[0]);
+		release_ioregs(cs, 7);
 		return (0);
 	} else {
-		request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo");
+		request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo");
 	}
-	if (check_region((sp->hscx[1]), 32)) {
+	if (check_region(cs->hw.avm.hscx[1] + 32, 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);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[1] + 32,
+		       cs->hw.avm.hscx[1] + 64);
+		release_ioregs(cs, 0xf);
 		return (0);
 	} else {
-		request_region(sp->hscx[1], 32, "HiSax hscx B");
+		request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B");
 	}
-	if (check_region((sp->hscx[1] - 0x400), 1)) {
+	if (check_region(cs->hw.avm.hscxfifo[1], 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);
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[1]);
+		release_ioregs(cs, 0x1f);
 		return (0);
 	} else {
-		request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo");
+		request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo");
 	}
 	save_flags(flags);
-	byteout(sp->cfg_reg, 0x0);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
 	sti();
 	HZDELAY(HZ / 5 + 1);
-	byteout(sp->cfg_reg, 0x1);
+	byteout(cs->hw.avm.cfg_reg, 0x1);
 	HZDELAY(HZ / 5 + 1);
-	byteout(sp->cfg_reg, 0x0);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
 	HZDELAY(HZ / 5 + 1);
-	val = sp->irq;
+	val = cs->irq;
 	if (val == 9)
 		val = 2;
-	byteout(sp->cfg_reg + 1, val);
+	byteout(cs->hw.avm.cfg_reg + 1, val);
 	HZDELAY(HZ / 5 + 1);
-	byteout(sp->cfg_reg, 0x0);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
 	HZDELAY(HZ / 5 + 1);
 	restore_flags(flags);
 
-	val = bytein(sp->cfg_reg);
+	val = bytein(cs->hw.avm.cfg_reg);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
-	       sp->cfg_reg, val);
-	val = bytein(sp->cfg_reg + 3);
+	       cs->hw.avm.cfg_reg, val);
+	val = bytein(cs->hw.avm.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);
+	       cs->hw.avm.cfg_reg + 3, val);
+	val = bytein(cs->hw.avm.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);
+	       cs->hw.avm.cfg_reg + 2, val);
+	val = bytein(cs->hw.avm.cfg_reg);
 	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
-	       sp->cfg_reg, val);
+	       cs->hw.avm.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_INFO
+	       "HiSax: %s config irq:%d cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.avm.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: isac:0x%X/0x%X\n",
+	       cs->hw.avm.isac + 32, cs->hw.avm.isacfifo);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X/0x%X  hscx B:0x%X/0x%X\n",
+	       cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0],
+	       cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+	ISACVersion(cs, "AVM A1:");
+	if (HscxVersion(cs, "AVM A1:")) {
 		printk(KERN_WARNING
 		       "AVM A1: wrong HSCX versions check IO address\n");
-		release_io_avm_a1(card);
+		release_ioregs(cs, 0x3f);
 		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.35/linux/drivers/isdn/hisax/avm_a1p.c linux/drivers/isdn/hisax/avm_a1p.c
--- v2.0.35/linux/drivers/isdn/hisax/avm_a1p.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_a1p.c	Sun Nov 15 10:32:57 1998
@@ -0,0 +1,331 @@
+/* $Id: avm_a1p.c,v 1.1.2.3 1998/11/03 00:05:47 keil Exp $
+ *
+ * avm_a1p.c    low level stuff for the following AVM cards:
+ *              A1 PCMCIA
+ *		FRITZ!Card PCMCIA
+ *		FRITZ!Card PCMCIA 2.0
+ *
+ * Author       Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: avm_a1p.c,v $
+ * Revision 1.1.2.3  1998/11/03 00:05:47  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.2  1998/09/27 13:05:33  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.1.2.1  1998/07/15 14:43:26  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+/* register offsets */
+#define ADDRREG_OFFSET		0x02
+#define DATAREG_OFFSET		0x03
+#define ASL0_OFFSET		0x04
+#define ASL1_OFFSET		0x05
+#define MODREG_OFFSET		0x06
+#define VERREG_OFFSET		0x07
+
+/* address offsets */
+#define ISAC_FIFO_OFFSET	0x00
+#define ISAC_REG_OFFSET		0x20
+#define HSCX_CH_DIFF		0x40
+#define HSCX_FIFO_OFFSET	0x80
+#define HSCX_REG_OFFSET		0xa0
+
+/* read bits ASL0 */
+#define	 ASL0_R_TIMER		0x10 /* active low */
+#define	 ASL0_R_ISAC		0x20 /* active low */
+#define	 ASL0_R_HSCX		0x40 /* active low */
+#define	 ASL0_R_TESTBIT		0x80
+#define  ASL0_R_IRQPENDING	(ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER)
+
+/* write bits ASL0 */
+#define	 ASL0_W_RESET		0x01
+#define	 ASL0_W_TDISABLE	0x02
+#define	 ASL0_W_TRESET		0x04
+#define	 ASL0_W_IRQENABLE	0x08
+#define	 ASL0_W_TESTBIT		0x80
+
+/* write bits ASL1 */
+#define	 ASL1_W_LED0		0x10
+#define	 ASL1_W_LED1		0x20
+#define	 ASL1_W_ENABLE_S0	0xC0
+ 
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static const char *avm_revision = "$Revision: 1.1.2.3 $";
+
+static inline u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	long flags;
+        u_char ret;
+
+        offset -= 0x20;
+	save_flags(flags);
+	cli();
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	restore_flags(flags);
+	return ret;
+}
+
+static inline void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	long flags;
+
+        offset -= 0x20;
+
+	save_flags(flags);
+	cli();
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+	restore_flags(flags);
+}
+
+static inline void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+static inline void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+static inline u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	u_char ret;
+	long flags;
+
+        offset -= 0x20;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	restore_flags(flags);
+	return ret;
+}
+
+static inline void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	long flags;
+
+        offset -= 0x20;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+	restore_flags(flags);
+}
+
+static inline void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+static inline void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) 
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "AVM A1 PCMCIA: Spurious interrupt!\n");
+		return;
+	}
+	while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
+		if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
+		if (sval & ASL0_R_HSCX) {
+                        val = ReadHSCX(cs, 1, HSCX_ISTA);
+			if (val) {
+				hscx_int_main(cs, val);
+				stat |= 1;
+			}
+		}
+		if (sval & ASL0_R_ISAC) {
+			val = ReadISAC(cs, ISAC_ISTA);
+			if (val) {
+				isac_interrupt(cs, val);
+				stat |= 2;
+			}
+		}
+	}
+	if (stat & 1) {
+		WriteHSCX(cs, 0, HSCX_MASK, 0xff);
+		WriteHSCX(cs, 1, HSCX_MASK, 0xff);
+		WriteHSCX(cs, 0, HSCX_MASK, 0x00);
+		WriteHSCX(cs, 1, HSCX_MASK, 0x00);
+	}
+	if (stat & 2) {
+		WriteISAC(cs, ISAC_MASK, 0xff);
+		WriteISAC(cs, ISAC_MASK, 0x00);
+	}
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	int ret;
+	switch (mt) {
+		case CARD_RESET:
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			return 0;
+
+		case CARD_RELEASE:
+			/* free_irq is done in HiSax_closecard(). */
+		        /* free_irq(cs->irq, cs); */
+			return 0;
+
+		case CARD_SETIRQ:
+			ret = request_irq(cs->irq, &avm_a1p_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs);
+			if (ret)
+				return ret;
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,
+				ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE);
+                        return 0;
+
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			inithscxisac(cs, 1);
+			inithscxisac(cs, 2);
+			return 0;
+
+		case CARD_TEST:
+			/* we really don't need it for the PCMCIA Version */
+			return 0;
+
+		default:
+			/* all card drivers ignore others, so we do the same */
+			return 0;
+	}
+	return 0;
+}
+
+__initfunc(int
+setup_avm_a1_pcmcia(struct IsdnCard *card))
+{
+	u_char model, vers;
+	struct IsdnCardState *cs = card->cs;
+	long flags;
+	char tmp[64];
+
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
+						 HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
+		return (0);
+
+	cs->hw.avm.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+
+
+	save_flags(flags);
+	outb(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0);
+        sti();
+
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET);
+
+	restore_flags(flags);
+
+	model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET);
+	vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET);
+
+	printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
+				cs->hw.avm.cfg_reg, cs->irq, model, vers);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+
+	ISACVersion(cs, "AVM A1 PCMCIA:");
+	if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
+		printk(KERN_WARNING
+		       "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/avm_pci.c linux/drivers/isdn/hisax/avm_pci.c
--- v2.0.35/linux/drivers/isdn/hisax/avm_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_pci.c	Sun Nov 15 10:32:57 1998
@@ -0,0 +1,880 @@
+/* $Id: avm_pci.c,v 1.1.2.8 1998/11/05 21:11:12 keil Exp $
+
+ * avm_pci.c    low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
+ *              Thanks to AVM, Berlin for informations
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: avm_pci.c,v $
+ * Revision 1.1.2.8  1998/11/05 21:11:12  keil
+ * AVM PnP support
+ *
+ * Revision 1.1.2.7  1998/11/03 00:05:48  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.6  1998/10/16 12:46:03  keil
+ * fix pci detection for more as one card
+ *
+ * Revision 1.1.2.5  1998/10/13 18:38:50  keil
+ * Fix PCI detection
+ *
+ * Revision 1.1.2.4  1998/10/04 23:03:41  keil
+ * PCI has 255 device entries
+ *
+ * Revision 1.1.2.3  1998/09/27 23:52:57  keil
+ * Fix error handling
+ *
+ * Revision 1.1.2.2  1998/09/27 13:03:16  keil
+ * Fix segfaults on connect
+ *
+ * Revision 1.1.2.1  1998/08/25 14:01:24  calle
+ * Ported driver for AVM Fritz!Card PCI from the 2.1 tree.
+ * I could not test it.
+ *
+ * Revision 1.1  1998/08/20 13:47:30  keil
+ * first version
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+static const char *avm_pci_rev = "$Revision: 1.1.2.8 $";
+
+#define  AVM_FRITZ_PCI		1
+#define  AVM_FRITZ_PNP		2
+
+#define  PCI_VENDOR_AVM		0x1244
+#define  PCI_FRITZPCI_ID	0xa00
+
+#define  HDLC_FIFO		0x0
+#define  HDLC_STATUS		0x4
+
+#define	 AVM_HDLC_1		0x00
+#define	 AVM_HDLC_2		0x01
+#define	 AVM_ISAC_FIFO		0x02
+#define	 AVM_ISAC_REG_LOW	0x04
+#define	 AVM_ISAC_REG_HIGH	0x06
+
+#define  AVM_STATUS0_IRQ_ISAC	0x01
+#define  AVM_STATUS0_IRQ_HDLC	0x02
+#define  AVM_STATUS0_IRQ_TIMER	0x04
+#define  AVM_STATUS0_IRQ_MASK	0x07
+
+#define  AVM_STATUS0_RESET	0x01
+#define  AVM_STATUS0_DIS_TIMER	0x02
+#define  AVM_STATUS0_RES_TIMER	0x04
+#define  AVM_STATUS0_ENA_IRQ	0x08
+#define  AVM_STATUS0_TESTBIT	0x10
+
+#define  AVM_STATUS1_INT_SEL	0x0f
+#define  AVM_STATUS1_ENA_IOM	0x80
+
+#define  HDLC_MODE_ITF_FLG	0x01
+#define  HDLC_MODE_TRANS	0x02
+#define  HDLC_MODE_CCR_7	0x04
+#define  HDLC_MODE_CCR_16	0x08
+#define  HDLC_MODE_TESTLOOP	0x80
+
+#define  HDLC_INT_XPR		0x80
+#define  HDLC_INT_XDU		0x40
+#define  HDLC_INT_RPR		0x20
+#define  HDLC_INT_MASK		0xE0
+
+#define  HDLC_STAT_RME		0x01
+#define  HDLC_STAT_RDO		0x10
+#define  HDLC_STAT_CRCVFRRAB	0x0E
+#define  HDLC_STAT_CRCVFR	0x06
+#define  HDLC_STAT_RML_MASK	0x3f00
+
+#define  HDLC_CMD_XRS		0x80
+#define  HDLC_CMD_XME		0x01
+#define  HDLC_CMD_RRS		0x20
+#define  HDLC_CMD_XML_MASK	0x3f00
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+	register u_char val;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + (offset & 0xf));
+	restore_flags(flags);
+	return (val);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + (offset & 0xf));
+	restore_flags(flags);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	insb(cs->hw.avm.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	outsb(cs->hw.avm.isac, data, size);
+}
+
+static inline u_int
+ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_int val;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	val = inl(cs->hw.avm.isac + offset);
+	restore_flags(flags);
+	return (val);
+}
+
+static inline void
+WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	outl(value, cs->hw.avm.isac + offset);
+	restore_flags(flags);
+}
+
+static inline u_char
+ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_char val;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + offset);
+	restore_flags(flags);
+	return (val);
+}
+
+static inline void
+WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register long flags;
+
+	save_flags(flags);
+	cli();
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + offset);
+	restore_flags(flags);
+}
+
+static u_char
+ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	return(0xff & ReadHDLCPCI(cs, chan, offset));
+}
+
+static void
+WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	WriteHDLCPCI(cs, chan, offset, value);
+}
+
+static inline
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void inline
+hdlc_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+void
+write_ctrl(struct BCState *bcs, int which) {
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "hdlc %c wr%x ctrl %x",
+			'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl);
+	if (bcs->cs->subtyp == AVM_FRITZ_PCI) {
+		WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl);
+	} else {
+		if (which & 4)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2,
+				bcs->hw.hdlc.ctrl.sr.mode);
+		if (which & 2)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1,
+				bcs->hw.hdlc.ctrl.sr.xml);
+		if (which & 1)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS,
+				bcs->hw.hdlc.ctrl.sr.cmd);
+	}
+}
+
+void
+modehdlc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hdlc = bcs->channel;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hdlc %c mode %d ichan %d",
+			'A' + hdlc, mode, bc);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	bcs->hw.hdlc.ctrl.ctrl = 0;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			break;
+		case (L1_MODE_TRANS):
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			hdlc_sched_event(bcs, B_XMTBUFREADY);
+			break;
+		case (L1_MODE_HDLC):
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			hdlc_sched_event(bcs, B_XMTBUFREADY);
+			break;
+	}
+}
+
+static inline void
+hdlc_empty_fifo(struct BCState *bcs, int count)
+{
+	register u_int *ptr;
+	u_char *p;
+	u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1;
+	int cnt=0;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_empty_fifo %d", count);
+	if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hdlc_empty_fifo: incoming packet too large");
+		return;
+	}
+	ptr = (u_int *) p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
+	bcs->hw.hdlc.rcvidx += count;
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		outl(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+			*ptr++ = inl(cs->hw.avm.isac);
+			cnt += 4;
+		}
+	} else {
+		outb(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+			*p++ = inb(cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+hdlc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count, cnt =0;
+	int fifo_size = 32;
+	u_char *p;
+	u_int *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (bcs->tx_skb->len > fifo_size) {
+		count = fifo_size;
+	} else {
+		count = bcs->tx_skb->len;
+		if (bcs->mode != L1_MODE_TRANS)
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len);
+	ptr = (u_int *) p = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hdlc.count += count;
+	bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+	write_ctrl(bcs, 3);  /* sets the correct index too */
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		while (cnt<count) {
+			outl(*ptr++, cs->hw.avm.isac);
+			cnt += 4;
+		}
+	} else {
+		while (cnt<count) {
+			outb(*p++, cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+fill_hdlc(struct BCState *bcs)
+{
+	long flags;
+	save_flags(flags);
+	cli();
+	hdlc_fill_fifo(bcs);
+	restore_flags(flags);
+}
+
+static inline void
+HDLC_irq(struct BCState *bcs, u_int stat) {
+	int len;
+	struct sk_buff *skb;
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+	if (stat & HDLC_INT_RPR) {
+		if (stat & HDLC_STAT_RDO) {
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "RDO");
+			else
+				debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+			bcs->hw.hdlc.ctrl.sr.xml = 0;
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.rcvidx = 0;
+		} else {
+			if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
+				len = 32;
+			hdlc_empty_fifo(bcs, len);
+			if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+				if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
+					(bcs->mode == L1_MODE_TRANS)) {
+					if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx)))
+						printk(KERN_WARNING "HDLC: receive out of memory\n");
+					else {
+						memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx),
+							bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx);
+						skb_queue_tail(&bcs->rqueue, skb);
+					}
+					bcs->hw.hdlc.rcvidx = 0;
+					hdlc_sched_event(bcs, B_RCVBUFREADY);
+				} else {
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "invalid frame");
+					else
+						debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat);
+					bcs->hw.hdlc.rcvidx = 0;
+				}
+			}
+		}
+	}
+	if (stat & HDLC_INT_XDU) {
+		/* Here we lost an TX interrupt, so
+		 * restart transmitting the whole frame.
+		 */
+		if (bcs->tx_skb) {
+			skb_push(bcs->tx_skb, bcs->hw.hdlc.count);
+			bcs->tx_cnt += bcs->hw.hdlc.count;
+			bcs->hw.hdlc.count = 0;
+//			hdlc_sched_event(bcs, B_XMTBUFREADY);
+			if (bcs->cs->debug & L1_DEB_WARN)
+				debugl1(bcs->cs, "ch%d XDU", bcs->channel);
+		} else if (bcs->cs->debug & L1_DEB_WARN)
+			debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel);
+		bcs->hw.hdlc.ctrl.sr.xml = 0;
+		bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		hdlc_fill_fifo(bcs);
+	} else if (stat & HDLC_INT_XPR) {
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				hdlc_fill_fifo(bcs);
+				return;
+			} else {
+				if (bcs->st->lli.l1writewakeup &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hdlc.count);
+				dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+				bcs->hw.hdlc.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hdlc.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hdlc_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			hdlc_sched_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+inline void
+HDLC_irq_main(struct IsdnCardState *cs)
+{
+	u_int stat;
+	long  flags;
+	struct BCState *bcs;
+
+	save_flags(flags);
+	cli();
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 0))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 0 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 1))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 1 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+	restore_flags(flags);
+}
+
+void
+hdlc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				st->l1.bcs->hw.hdlc.count = 0;
+				restore_flags(flags);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->hw.hdlc.count = 0;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			modehdlc(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			modehdlc(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_hdlcstate(struct BCState *bcs)
+{
+	modehdlc(bcs, 0, 0);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hdlc.rcvbuf) {
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+int
+open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hdlc.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hdlc.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hdlc(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hdlcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hdlc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_hdlc_ints(struct IsdnCardState *cs))
+{
+	u_int val;
+
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		val = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+	} else {
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 1 RML %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 1 MODE %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 1 VIN %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 2 RML %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 2 MODE %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 2 VIN %x", val);
+	}
+}
+
+HISAX_INITFUNC(void
+inithdlc(struct IsdnCardState *cs))
+{
+	cs->bcs[0].BC_SetStack = setstack_hdlc;
+	cs->bcs[1].BC_SetStack = setstack_hdlc;
+	cs->bcs[0].BC_Close = close_hdlcstate;
+	cs->bcs[1].BC_Close = close_hdlcstate;
+	modehdlc(cs->bcs, 0, 0);
+	modehdlc(cs->bcs + 1, 0, 0);
+}
+
+static void
+avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+	u_char sval;
+
+	if (!cs) {
+		printk(KERN_WARNING "AVM PCI: Spurious interrupt!\n");
+		return;
+	}
+	sval = inb(cs->hw.avm.cfg_reg + 2);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+		/* possible a shared  IRQ reqest */
+		return;
+	if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+		val = ReadISAC(cs, ISAC_ISTA);
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
+		HDLC_irq_main(cs);
+	}
+	if (stat & 2) {
+		WriteISAC(cs, ISAC_MASK, 0xFF);
+		WriteISAC(cs, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+reset_avmpcipnp(struct IsdnCardState *cs)
+{
+	long flags;
+
+	printk(KERN_INFO "AVM PCI/PnP: reset\n");
+	save_flags(flags);
+	sti();
+	outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+	outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_int irq_flag;
+
+	switch (mt) {
+		case CARD_RESET:
+			reset_avmpcipnp(cs);
+			return(0);
+		case CARD_RELEASE:
+			outb(0, cs->hw.avm.cfg_reg + 2);
+			release_region(cs->hw.avm.cfg_reg, 32);
+			return(0);
+		case CARD_SETIRQ:
+			if (cs->subtyp == AVM_FRITZ_PCI)
+				irq_flag = I4L_IRQ_FLAG | SA_SHIRQ;
+			else
+				irq_flag = I4L_IRQ_FLAG;
+			return(request_irq(cs->irq, &avm_pcipnp_interrupt,
+					irq_flag, "HiSax", cs));
+		case CARD_INIT:
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			clear_pending_hdlc_ints(cs);
+			inithdlc(cs);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER,
+				cs->hw.avm.cfg_reg + 2);
+			WriteISAC(cs, ISAC_MASK, 0);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+				AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+			/* RESET Receiver and Transmitter */
+			WriteISAC(cs, ISAC_CMDR, 0x41);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_avm_pcipnp(struct IsdnCard *card))
+{
+	u_int val, ver;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, avm_pci_rev);
+	printk(KERN_INFO "HiSax: AVM PCI/ISAPnP driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_FRITZPCI)
+		return (0);
+	if (card->para[1]) {
+		cs->hw.avm.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = AVM_FRITZ_PNP;
+	} else {
+#if CONFIG_PCI
+		for (; pci_index < 255; pci_index++) {
+			unsigned char pci_bus, pci_device_fn;
+			unsigned int ioaddr;
+			unsigned char irq;
+
+			if (pcibios_find_device (PCI_VENDOR_AVM,
+				PCI_FRITZPCI_ID, 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);
+			cs->irq = irq;
+			cs->hw.avm.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK;
+			if (!cs->hw.avm.cfg_reg) {
+				printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			cs->subtyp = AVM_FRITZ_PCI;
+			break;
+		}
+		if (pci_index == 255) {
+			printk(KERN_WARNING "FritzPCI: No PCI card found\n");
+			return(0);
+        	}
+	        pci_index++;
+#else
+		printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+	cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
+	if (check_region((cs->hw.avm.cfg_reg), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 31);
+		return (0);
+	} else {
+		request_region(cs->hw.avm.cfg_reg, 32,
+			(cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP");
+	}
+	switch (cs->subtyp) {
+	  case AVM_FRITZ_PCI:
+		val = inl(cs->hw.avm.cfg_reg);
+		printk(KERN_INFO "AVM PCI: stat %#x\n", val);
+		printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
+			val & 0xff, (val>>8) & 0xff);
+		cs->BC_Read_Reg = &ReadHDLC_s;
+		cs->BC_Write_Reg = &WriteHDLC_s;
+		break;
+	  case AVM_FRITZ_PNP:
+		val = inb(cs->hw.avm.cfg_reg);
+		ver = inb(cs->hw.avm.cfg_reg + 1);
+		printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
+		reset_avmpcipnp(cs);
+		cs->BC_Read_Reg = &ReadHDLCPnP;
+		cs->BC_Write_Reg = &WriteHDLCPnP;
+		break;
+	  default:
+	  	printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
+	  	return(0);
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n",
+		(cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP",
+		cs->irq, cs->hw.avm.cfg_reg);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Send_Data = &fill_hdlc;
+	cs->cardmsg = &AVM_card_msg;
+	ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- v2.0.35/linux/drivers/isdn/hisax/callc.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/callc.c	Sun Nov 15 10:32:58 1998
@@ -1,156 +1,146 @@
-/* $Id: callc.c,v 1.30 1997/05/29 10:40:43 keil Exp $
+/* $Id: callc.c,v 1.30.2.13 1998/11/05 21:13:32 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * 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.30.2.13  1998/11/05 21:13:32  keil
+ * minor fixes
  *
- * Revision 1.23  1997/03/04 23:07:42  keil
- * bugfix dial parameter
+ * Revision 1.30.2.12  1998/11/03 00:05:51  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.22  1997/02/27 13:51:55  keil
- * Reset B-channel (dlc) statemachine in every release
+ * Revision 1.30.2.11  1998/09/30 22:20:05  keil
+ * Cosmetics
  *
- * Revision 1.21  1997/02/19 09:24:27  keil
- * Bugfix: Hangup to LL if a ttyI rings
+ * Revision 1.30.2.10  1998/09/27 13:05:35  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
- * Revision 1.20  1997/02/17 00:32:47  keil
- * Bugfix: No Busy reported to LL
+ * Revision 1.30.2.9  1998/05/27 18:04:53  keil
+ * HiSax 3.0
  *
- * Revision 1.19  1997/02/14 12:23:10  fritz
- * Added support for new insmod parameter handling.
+ * Revision 1.30.2.8  1998/04/11 18:48:26  keil
+ * remove debug
  *
- * Revision 1.18  1997/02/11 01:36:58  keil
- * Changed setup-interface (incoming and outgoing), cause reporting
+ * Revision 1.30.2.7  1998/04/08 21:51:50  keil
+ * new debug
  *
- * 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.30.2.6  1998/03/07 23:15:02  tsbogend
+ * made HiSax working on Linux/Alpha
  *
- * Revision 1.16  1997/01/27 23:17:03  keil
- * delete timers while unloading
+ * Revision 1.30.2.5  1998/02/09 11:24:17  keil
+ * New leased line support (Read README.HiSax!)
  *
- * Revision 1.15  1997/01/27 16:00:38  keil
- * D-channel shutdown delay; improved callback
+ * Revision 1.30.2.4  1998/01/27 22:46:00  keil
+ * B-channel send delay now configurable
  *
- * Revision 1.14  1997/01/21 22:16:39  keil
- * new statemachine; leased line support; cleanup for 2.0
+ * Revision 1.30.2.3  1998/01/11 23:21:03  keil
+ * Missing callc state
  *
- * Revision 1.13  1996/12/08 19:51:17  keil
- * bugfixes from Pekka Sarnila
+ * Revision 1.30.2.2  1997/11/15 18:54:31  keil
+ * cosmetics
  *
- * Revision 1.12  1996/11/26 20:20:03  keil
- * fixed warning while compile
+ * Revision 1.30.2.1  1997/10/17 22:13:32  keil
+ * update to last hisax version
  *
- * Revision 1.11  1996/11/26 18:43:17  keil
- * change ioctl 555 --> 55 (555 didn't work)
+ * Revision 2.6  1997/09/11 17:26:58  keil
+ * Open B-channel if here are incomming packets
  *
- * Revision 1.10  1996/11/26 18:06:07  keil
- * fixed missing break statement,ioctl 555 reset modcount
+ * Revision 2.5  1997/08/07 17:46:05  keil
+ * Fix Incomming Call without broadcast
  *
- * Revision 1.9  1996/11/18 20:23:19  keil
- * log writebuf channel not open changed
+ * Revision 2.4  1997/08/03 14:37:58  keil
+ * Activate Layer2 in PtP mode
  *
- * Revision 1.8  1996/11/06 17:43:17  keil
- * more changes for 2.1.X;block fixed ST_PRO_W
+ * Revision 2.3  1997/07/31 19:23:40  keil
+ * LAYER2_WATCHING for PtP
  *
- * Revision 1.7  1996/11/06 15:13:51  keil
- * typo 0x64 --->64 in debug code
+ * Revision 2.2  1997/07/31 11:48:18  keil
+ * experimental REJECT after ALERTING
  *
- * Revision 1.6  1996/11/05 19:40:33  keil
- * X.75 windowsize
+ * Revision 2.1  1997/07/30 17:12:59  keil
+ * more changes for 'One TEI per card'
  *
- * Revision 1.5  1996/10/30 10:11:06  keil
- * debugging LOCK changed;ST_REL_W EV_HANGUP added
+ * Revision 2.0  1997/07/27 21:12:21  keil
+ * CRef based L3; new channel handling; many other stuff
  *
- * Revision 1.4  1996/10/27 22:20:16  keil
- * alerting bugfixes
- * no static b-channel<->channel mapping
+ * Revision 1.31  1997/06/26 11:09:23  keil
+ * New managment and minor changes
  *
- * 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
+ * old logs removed /KKe
  *
  */
 
 #define __NO_VERSION__
 #include "hisax.h"
+#include "../avmb1/capicmd.h"  /* this should be moved in a common place */
 
 #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 $";
+const char *lli_revision = "$Revision: 1.30.2.13 $";
 
 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 int init_b_st(struct Channel *chanp, int incoming);
+static void release_b_st(struct Channel *chanp);
 
 static struct Fsm callcfsm =
-{NULL, 0, 0};
-static struct Fsm lcfsm =
-{NULL, 0, 0};
+{NULL, 0, 0, NULL, NULL};
 
 static int chancount = 0;
 
-/* Flags for remembering action done in l4 */
+/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
+#define ALERT_REJECT 0
+
+/* Value to delay the sending of the first B-channel paket after CONNECT
+ * here is no value given by ITU, but experience shows that 300 ms will
+ * work on many networks, if you or your other side is behind local exchanges
+ * a greater value may be recommented. If the delay is to short the first paket
+ * will be lost and autodetect on many comercial routers goes wrong !
+ * You can adjust this value on runtime with
+ * hisaxctrl <id> 2 <value>
+ * value is in milliseconds
+ */
+#define DEFAULT_B_DELAY	300
 
-#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
+/* Flags for remembering action done in lli */
 
-#define  SETBIT(flg, item)  flg |= item
-#define  RESBIT(flg, item)  flg &= (~item)
+#define  FLG_START_D	0
+#define  FLG_ESTAB_D	1
+#define  FLG_CALL_SEND	2
+#define  FLG_CALL_REC   3
+#define  FLG_CALL_ALERT	4
+#define  FLG_START_B	5
+#define  FLG_CONNECT_B	6
+#define  FLG_LL_DCONN	7
+#define  FLG_LL_BCONN	8
+#define  FLG_DISC_SEND	9
+#define  FLG_DISC_REC	10
+#define  FLG_REL_REC	11
+#define  FLG_DO_ALERT	12
+#define  FLG_DO_HANGUP	13
+#define  FLG_DO_CONNECT	14
+#define  FLG_DO_ESTAB	15
+#define  FLG_RESUME	16
 
 /*
  * Because of callback it's a good idea to delay the shutdown of the d-channel
  */
-#define	DREL_TIMER_VALUE 30000
+#define	DREL_TIMER_VALUE 40000
 
 /*
  * Find card with given driverId
@@ -162,21 +152,36 @@
 	int i;
 
 	for (i = 0; i < nrcards; i++)
-		if (cards[i].sp)
-			if (cards[i].sp->myid == driverid)
-				return (cards[i].sp);
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return (cards[i].cs);
 	return (struct IsdnCardState *) 0;
 }
 
+int
+discard_queue(struct sk_buff_head *q)
+{
+	struct sk_buff *skb;
+	int ret=0;
+
+	while ((skb = skb_dequeue(q))) {
+		dev_kfree_skb(skb, FREE_READ);
+		ret++;
+	}
+	return(ret);
+}
+
 static void
-link_debug(struct Channel *chanp, char *s, int direction)
+link_debug(struct Channel *chanp, int direction, char *fmt, ...)
 {
-	char tmp[100], tm[32];
+	va_list args;
+	char tmp[16];
 
-	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);
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d %s ", chanp->chan,
+		direction ? "LL->HL" : "HL->LL");
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
 }
 
 
@@ -233,7 +238,7 @@
 	EV_SETUP_CMPL_IND,	/* 10 */
 	EV_BC_EST,		/* 11 */
 	EV_WRITEBUF,		/* 12 */
-	EV_DATAIN,		/* 13 */
+	EV_ESTABLISH,		/* 13 */
 	EV_HANGUP,		/* 14 */
 	EV_BC_REL,		/* 15 */
 	EV_CINF,		/* 16 */
@@ -263,7 +268,7 @@
 	"EV_SETUP_CMPL_IND",
 	"EV_BC_EST",
 	"EV_WRITEBUF",
-	"EV_DATAIN",
+	"EV_ESTABLISH",
 	"EV_HANGUP",
 	"EV_BC_REL",
 	"EV_CINF",
@@ -276,1105 +281,1021 @@
 	"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[] =
+static inline void
+lli_deliver_cause(struct Channel *chanp, isdn_ctrl *ic)
 {
-	"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)
+	if (chanp->proc->para.cause < 0)
+		return;
+	ic->driver = chanp->cs->myid;
+	ic->command = ISDN_STAT_CAUSE;
+	ic->arg = chanp->chan;
+	if (chanp->cs->protocol == ISDN_PTYPE_EURO)
+		sprintf(ic->parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
+	else
+		sprintf(ic->parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
+	chanp->cs->iif.statcallb(ic);
+}
 
-static char *strLcEvent[] =
+static void
+lli_d_established(struct FsmInst *fi, int event, void *arg)
 {
-	"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",
-};
+	struct Channel *chanp = fi->userdata;
 
-#define LC_D  0
-#define LC_B  1
+	test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+	if (chanp->leased) {
+		isdn_ctrl ic;
+		int ret;
 
-static inline void
-l4_deliver_cause(struct Channel *chanp)
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+		FsmChangeState(fi, ST_IN_WAIT_LL);
+		test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, "STAT_ICALL_LEASED");
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_ICALL;
+		ic.arg = chanp->chan;
+		ic.parm.setup.si1 = 7;
+		ic.parm.setup.si2 = 0;
+		ic.parm.setup.plan = 0;
+		ic.parm.setup.screen = 0;
+		sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
+		sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
+		ret = chanp->cs->iif.statcallb(&ic);
+		if (chanp->debug & 1)
+			link_debug(chanp, 1, "statcallb ret=%d", ret);
+		if (!ret) {
+			chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+			FsmChangeState(fi, ST_NULL);
+		}
+	} else if (fi->state == ST_WAIT_DSHUTDOWN)
+		FsmChangeState(fi, ST_NULL);
+}
+
+static void
+lli_d_released(struct FsmInst *fi, int event, void *arg)
 {
-	isdn_ctrl ic;
+	struct Channel *chanp = fi->userdata;
 
-	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);
+	test_and_clear_bit(FLG_START_D, &chanp->Flags);
 }
 
 /*
  * Dial out
  */
 static void
-l4_prep_dialout(struct FsmInst *fi, int event, void *arg)
+lli_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) {
+	if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
 		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);
+		chanp->Flags = 0;
+		if (EV_RESUME == event)
+			test_and_set_bit(FLG_RESUME, &chanp->Flags);
+		test_and_set_bit(FLG_START_D, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
 	}
 }
 
 static void
-l4_do_dialout(struct FsmInst *fi, int event, void *arg)
+lli_do_dialout(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
+	int ev;
 
 	FsmChangeState(fi, ST_OUT_DIAL);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (test_and_clear_bit(FLG_RESUME, &chanp->Flags))
+		ev = CC_RESUME | REQUEST;
+	else
+		ev = CC_SETUP | REQUEST;
 	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);
+		test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, ev, chanp);
+		test_and_set_bit(FLG_CALL_SEND, &chanp->Flags);
 	}
 }
 
 static void
-l4_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DCONN", 0);
-	ic.driver = chanp->sp->myid;
+		link_debug(chanp, 0, "STAT_DCONN");
+	ic.driver = chanp->cs->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);
+	chanp->cs->iif.statcallb(&ic);
+	init_b_st(chanp, 0);
+	test_and_set_bit(FLG_START_B, &chanp->Flags);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
-l4_go_active(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	test_and_set_bit(FLG_CONNECT_B, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_BCONN", 0);
-	SETBIT(chanp->Flags, FLG_LL_BCONN);
-	ic.driver = chanp->sp->myid;
+		link_debug(chanp, 0, "STAT_BCONN");
+	test_and_set_bit(FLG_LL_BCONN, &chanp->Flags);
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_BCONN;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan);
 }
 
+/*
+ * RESUME
+ */
+
 /* incomming call */
 
 static void
-l4_start_dchan(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	if (event == EV_ACCEPTD)
+		test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags);
+	else if (event == EV_HANGUP) {
+		test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags);
+#ifdef ALERT_REJECT
+		test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
+#endif
 	}
+	if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
+		FsmEvent(fi, EV_DLEST, NULL);
+	} else if (!test_and_set_bit(FLG_START_D, &chanp->Flags))
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
 static void
-l4_deliver_call(struct FsmInst *fi, int event, void *arg)
+lli_deliver_call(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 	int ret;
-	char txt[32];
 
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
 	/*
 	 * 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)) {
+	if (1) { /* for only one TEI */
 		FsmChangeState(fi, ST_IN_WAIT_LL);
-		SETBIT(chanp->Flags, FLG_ESTAB_D);
-		SETBIT(chanp->Flags, FLG_CALL_REC);
+		test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_ICALL", 0);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_ICALL");
+		ic.driver = chanp->cs->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);
+		ic.parm.setup = chanp->proc->para.setup;
+		ret = chanp->cs->iif.statcallb(&ic);
+		if (chanp->debug & 1)
+			link_debug(chanp, 1, "statcallb ret=%d", ret);
 		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);
+				FsmDelTimer(&chanp->drel_timer, 61);
+				if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
+					FsmChangeState(fi, ST_IN_ALERT_SEND);
+					test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
+					chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+				} else {
+					test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
+					FsmChangeState(fi, ST_IN_WAIT_D);
+					test_and_set_bit(FLG_START_D, &chanp->Flags);
+					chanp->d_st->lli.l4l3(chanp->d_st,
+						DL_ESTABLISH | REQUEST, NULL);
+				}
 				break;
 			case 2:	/* Rejecting Call */
-				RESBIT(chanp->Flags, FLG_CALL_REC);
+				test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
 				break;
 			case 0:	/* OK, nobody likes this call */
 			default:	/* statcallb problems */
-				chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+				chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 				FsmChangeState(fi, ST_NULL);
-				chanp->Flags = FLG_ESTAB_D;
-				FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
+				if (test_bit(FLG_ESTAB_D, &chanp->Flags) &&
+					!test_bit(FLG_PTP, &chanp->d_st->l2.flag))
+					FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
 				break;
 		}
 	} else {
-		chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 		FsmChangeState(fi, ST_NULL);
-		chanp->Flags = FLG_ESTAB_D;
-		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
+		if (test_bit(FLG_ESTAB_D, &chanp->Flags) &&
+			!test_bit(FLG_PTP, &chanp->d_st->l2.flag))
+			FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
 	}
 }
 
 static void
-l4_send_dconnect(struct FsmInst *fi, int event, void *arg)
+lli_establish_d(struct FsmInst *fi, int event, void *arg)
+{
+	/* This establish the D-channel for pending L3 messages
+	 * without blocking the channel
+	 */
+	struct Channel *chanp = fi->userdata;
+
+	test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags);
+	FsmChangeState(fi, ST_IN_WAIT_D);
+	test_and_set_bit(FLG_START_D, &chanp->Flags);
+	chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lli_do_action(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+	if (chanp->leased) {
+		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+		test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags);
+		test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags);
+		FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+	} else if (test_and_clear_bit(FLG_DO_CONNECT, &chanp->Flags) &&
+		!test_bit(FLG_DO_HANGUP, &chanp->Flags)) {
+		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+		test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+	} else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) {
+		if (test_bit(FLG_DO_HANGUP, &chanp->Flags))
+			FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
+		FsmChangeState(fi, ST_IN_ALERT_SEND);
+		test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+	} else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		chanp->proc->para.cause = 0x15;		/* Call Rejected */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
+		test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
+	}
+}
+
+static void
+lli_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);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
 }
 
 static void
-l4_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DCONN", 0);
-	ic.driver = chanp->sp->myid;
+		link_debug(chanp, 0, "STAT_DCONN");
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DCONN;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->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);
+	init_b_st(chanp, !0);
+	test_and_set_bit(FLG_START_B, &chanp->Flags);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
 }
 
-/* Call clearing */
+/* Call suspend */
 
 static void
-l4_reject_call(struct FsmInst *fi, int event, void *arg)
+lli_suspend(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);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
 }
 
+/* Call clearing */
+
 static void
-l4_cancel_call(struct FsmInst *fi, int event, void *arg)
+lli_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 (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_BHUP");
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->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);
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	chanp->proc->para.cause = 0x10;		/* Normal Call Clearing */
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
+	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
 }
 
 static void
-l4_shutdown_d(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	if (test_bit(FLG_PTP, &chanp->d_st->l2.flag)) {
+		FsmChangeState(fi, ST_NULL);
+	} else {
+		if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) {
+			if (chanp->chan) {
+				if (chanp->cs->channel[0].fi.state != ST_NULL)
+					return;
+			} else {
+				if (chanp->cs->channel[1].fi.state != ST_NULL)
+					return;
+			}
+		}
+		FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+		test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
+	}
 }
 
 static void
-l4_timeout_d(struct FsmInst *fi, int event, void *arg)
+lli_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);
-	}
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
 	FsmChangeState(fi, ST_NULL);
-	chanp->Flags = FLG_ESTAB_D;
-	FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+	chanp->Flags = 0;
+	test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
+	if (!test_bit(FLG_PTP, &chanp->d_st->l2.flag))
+		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
 static void
-l4_go_null(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
 static void
-l4_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
-l4_send_d_disc(struct FsmInst *fi, int event, void *arg)
+lli_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))
+	if (test_bit(FLG_DISC_REC, &chanp->Flags) ||
+		test_bit(FLG_REL_REC, &chanp->Flags))
 		return;
 	FsmChangeState(fi, ST_WAIT_DRELEASE);
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_BHUP");
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	if (chanp->Flags & FLG_START_B) {
-		release_ds(chanp);
-		RESBIT(chanp->Flags, FLG_START_B);
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (chanp->leased) {
+		ic.command = ISDN_STAT_CAUSE;
+		ic.arg = chanp->chan;
+		sprintf(ic.parm.num, "L0010");
+		chanp->cs->iif.statcallb(&ic);
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, "STAT_DHUP");
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->cs->iif.statcallb(&ic);
+		FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+		test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
+	} else {
+		if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags))
+			chanp->proc->para.cause = 0x15;		/* Call Reject */
+		else
+			chanp->proc->para.cause = 0x10;		/* Normal Call Clearing */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
+		test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
 	}
-	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)
+lli_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 (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_BHUP");
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	release_ds(chanp);
-	RESBIT(chanp->Flags, FLG_START_B);
+	release_b_st(chanp);
+	test_and_clear_bit(FLG_START_B, &chanp->Flags);
 }
 
 
 static void
-l4_release_bchan(struct FsmInst *fi, int event, void *arg)
+lli_release_bchan(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 
 	chanp->data_open = 0;
-	SETBIT(chanp->Flags, FLG_DISC_REC);
+	test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
 	FsmChangeState(fi, ST_WAIT_BREL_DISC);
-	RESBIT(chanp->Flags, FLG_CONNECT_B);
-	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+	test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
-l4_received_d_rel(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	FsmChangeState(fi, ST_NULL);
+	test_and_set_bit(FLG_REL_REC, &chanp->Flags);
+	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
+		chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	}
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_BHUP");
+		ic.driver = chanp->cs->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);
+		chanp->cs->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);
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
+	lli_timeout_d(fi, event, arg);
 }
 
 static void
-l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	FsmChangeState(fi, ST_NULL);
+	if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
+		chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	}
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_BHUP");
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	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);
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
+	lli_timeout_d(fi, event, arg);
 }
 
 static void
-l4_received_d_disc(struct FsmInst *fi, int event, void *arg)
+lli_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) {
+	test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_BHUP");
+		ic.driver = chanp->cs->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);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	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);
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc);
 }
 
 /* processing charge info */
 static void
-l4_charge_info(struct FsmInst *fi, int event, void *arg)
+lli_charge_info(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
 	isdn_ctrl ic;
 
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_CINF;
 	ic.arg = chanp->chan;
-	sprintf(ic.parm.num, "%d", chanp->para.chargeinfo);
-	chanp->sp->iif.statcallb(&ic);
+	sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo);
+	chanp->cs->iif.statcallb(&ic);
 }
 
 /* error procedures */
 
 static void
-l4_no_dchan(struct FsmInst *fi, int event, void *arg)
+lli_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;
+		link_debug(chanp, 0, "STAT_NODCH");
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_NODCH;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
 	chanp->Flags = 0;
 	FsmChangeState(fi, ST_NULL);
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
-l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg)
+lli_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;
+		link_debug(chanp, 0, "STAT_DHUP");
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DHUP;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
 }
 
 static void
-l4_no_dchan_in(struct FsmInst *fi, int event, void *arg)
+lli_no_dchan_in(struct FsmInst *fi, int event, void *arg)
 {
 	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
 
-	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc);
 	chanp->Flags = 0;
 	FsmChangeState(fi, ST_NULL);
-	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 }
 
 static void
-l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+lli_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);
+	test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
 	if (chanp->debug & 1)
-		link_debug(chanp, "STAT_DHUP", 0);
-	ic.driver = chanp->sp->myid;
+		link_debug(chanp, 0, "STAT_DHUP");
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_DHUP;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+	chanp->cs->iif.statcallb(&ic);
+	lli_shutdown_d(fi, event, arg);
 }
 
 static void
-l4_setup_err(struct FsmInst *fi, int event, void *arg)
+lli_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 */
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);	/* DISCONN was sent from L3 */
 }
 
 static void
-l4_connect_err(struct FsmInst *fi, int event, void *arg)
+lli_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 */
+	test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	lli_deliver_cause(chanp, &ic);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);	/* DISCONN was sent from L3 */
 }
 
 static void
-l4_active_dlrl(struct FsmInst *fi, int event, void *arg)
+lli_got_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 (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
+		chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
 	}
-	if (chanp->Flags & FLG_LL_BCONN) {
+	if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
 		if (chanp->debug & 1)
-			link_debug(chanp, "STAT_BHUP", 0);
-		ic.driver = chanp->sp->myid;
+			link_debug(chanp, 0, "STAT_BHUP");
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_BHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
-		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		chanp->cs->iif.statcallb(&ic);
 	}
-	if (chanp->Flags & FLG_START_B) {
-		release_ds(chanp);
-		RESBIT(chanp->Flags, FLG_START_B);
-	}
-	if (chanp->Flags & FLG_LL_DCONN) {
+	if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
+		release_b_st(chanp);
+	if (chanp->leased) {
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_CAUSE;
+		ic.arg = chanp->chan;
+		sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f);
+		chanp->cs->iif.statcallb(&ic);
+		ic.driver = chanp->cs->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->cs->iif.statcallb(&ic);
+		chanp->Flags = 0;
+	} else {
+		test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
 		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;
+			link_debug(chanp, 0, "STAT_DHUP");
+		if (chanp->cs->protocol == ISDN_PTYPE_EURO) {
+			chanp->proc->para.cause = 0x2f;
+			chanp->proc->para.loc = 0;
 		} else {
-			chanp->para.cause = 0x70;
-			chanp->para.loc = 0;
+			chanp->proc->para.cause = 0x70;
+			chanp->proc->para.loc = 0;
 		}
-		l4_deliver_cause(chanp);
-		ic.driver = chanp->sp->myid;
+		lli_deliver_cause(chanp, &ic);
+		ic.driver = chanp->cs->myid;
 		ic.command = ISDN_STAT_DHUP;
 		ic.arg = chanp->chan;
-		chanp->sp->iif.statcallb(&ic);
+		chanp->cs->iif.statcallb(&ic);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc);
+		chanp->Flags = 0;
+		chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
 	}
-	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);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
 }
 
-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[] =
+static struct FsmNode fnlist[] HISAX_INITDATA =
 {
-	{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},
+	{ST_NULL,		EV_DIAL,		lli_prep_dialout},
+	{ST_NULL,		EV_RESUME,		lli_prep_dialout},
+	{ST_NULL,		EV_SETUP_IND,		lli_deliver_call},
+	{ST_NULL,		EV_SHUTDOWN_D,		lli_shutdown_d},
+	{ST_NULL,		EV_DLRL,		lli_go_null},
+	{ST_NULL,		EV_DLEST,		lli_d_established},
+	{ST_NULL,		EV_ESTABLISH,		lli_establish_d},
+	{ST_OUT_WAIT_D,		EV_DLEST,		lli_do_dialout},
+	{ST_OUT_WAIT_D,		EV_DLRL,		lli_no_dchan},
+	{ST_OUT_WAIT_D,		EV_HANGUP,		lli_no_dchan},
+	{ST_IN_WAIT_D,		EV_DLEST,		lli_do_action},
+	{ST_IN_WAIT_D,		EV_DLRL,		lli_no_dchan_in},
+	{ST_IN_WAIT_D,		EV_ACCEPTD,		lli_start_dchan},
+	{ST_IN_WAIT_D,		EV_HANGUP,		lli_start_dchan},
+	{ST_OUT_DIAL,		EV_SETUP_CNF,		lli_init_bchan_out},
+	{ST_OUT_DIAL,		EV_HANGUP,		lli_cancel_call},
+	{ST_OUT_DIAL,		EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_OUT_DIAL,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_OUT_DIAL,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_OUT_DIAL,		EV_NOSETUP_RSP,		lli_no_setup_rsp},
+	{ST_OUT_DIAL,		EV_SETUP_ERR,		lli_setup_err},
+	{ST_OUT_DIAL,		EV_DLRL,		lli_got_dlrl},
+	{ST_IN_WAIT_LL,		EV_DLEST,		lli_d_established},
+	{ST_IN_WAIT_LL,		EV_DLRL,		lli_d_released},
+	{ST_IN_WAIT_LL,		EV_ACCEPTD,		lli_start_dchan},
+	{ST_IN_WAIT_LL,		EV_HANGUP,		lli_start_dchan},
+	{ST_IN_WAIT_LL,		EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_IN_WAIT_LL,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_IN_WAIT_LL,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_IN_ALERT_SEND,	EV_SETUP_CMPL_IND,	lli_init_bchan_in},
+	{ST_IN_ALERT_SEND,	EV_ACCEPTD,		lli_send_dconnect},
+	{ST_IN_ALERT_SEND,	EV_HANGUP,		lli_send_d_disc},
+	{ST_IN_ALERT_SEND,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_IN_ALERT_SEND,	EV_DLRL,		lli_got_dlrl},
+	{ST_IN_WAIT_CONN_ACK,	EV_SETUP_CMPL_IND,	lli_init_bchan_in},
+	{ST_IN_WAIT_CONN_ACK,	EV_HANGUP,		lli_send_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_CONNECT_ERR,		lli_connect_err},
+	{ST_IN_WAIT_CONN_ACK,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BCONN,		EV_BC_EST,		lli_go_active},
+	{ST_WAIT_BCONN,		EV_BC_REL,		lli_send_d_disc},
+	{ST_WAIT_BCONN,		EV_HANGUP,		lli_send_d_disc},
+	{ST_WAIT_BCONN,		EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_WAIT_BCONN,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_BCONN,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_BCONN,		EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BCONN,		EV_CINF,		lli_charge_info},
+	{ST_ACTIVE,		EV_CINF,		lli_charge_info},
+	{ST_ACTIVE,		EV_BC_REL,		lli_released_bchan},
+	{ST_ACTIVE,		EV_SUSPEND,		lli_suspend},
+	{ST_ACTIVE,		EV_HANGUP,		lli_disconn_bchan},
+	{ST_ACTIVE,		EV_DISCONNECT_IND,	lli_release_bchan},
+	{ST_ACTIVE,		EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_ACTIVE,		EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_ACTIVE,		EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BRELEASE,	EV_BC_REL,		lli_send_d_disc},
+	{ST_WAIT_BRELEASE,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_BRELEASE,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_BREL_DISC,	EV_BC_REL,		lli_received_d_disc},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_BREL_DISC,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_DCOMMAND,	EV_HANGUP,		lli_send_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_DISCONNECT_IND,	lli_received_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_CNF,		lli_received_d_relcnf},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_IND,		lli_received_d_rel},
+	{ST_WAIT_DCOMMAND,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_IND,		lli_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_CNF,		lli_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_ERR,		lli_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_DIAL,		lli_no_dchan_ready},
+	{ST_WAIT_DRELEASE,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_CNF,		lli_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_ERR,		lli_timeout_d},
+/* ETS 300-104 16.1 */
+	{ST_WAIT_D_REL_CNF,     EV_RELEASE_IND,         lli_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_DIAL,		lli_no_dchan_ready},
+	{ST_WAIT_D_REL_CNF,	EV_DLRL,		lli_got_dlrl},
+	{ST_WAIT_DSHUTDOWN,	EV_DLRL,		lli_go_null},
+	{ST_WAIT_DSHUTDOWN,	EV_DLEST,		lli_d_established},
+	{ST_WAIT_DSHUTDOWN,	EV_DIAL,		lli_prep_dialout},
+	{ST_WAIT_DSHUTDOWN,	EV_RESUME,		lli_prep_dialout},
+	{ST_WAIT_DSHUTDOWN,	EV_SETUP_IND,		lli_deliver_call},
 };
 /* *INDENT-ON* */
 
 
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
 
-
-
-
-
-
-
-
-#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
-
-void
-CallcNew(void)
+HISAX_INITFUNC(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)
+release_b_st(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);
+	struct PStack *st = chanp->b_st;
 
+	chanp->bcs->BC_Close(chanp->bcs);
 	switch (chanp->l2_active_protocol) {
 		case (ISDN_PROTO_L2_X75I):
 			releasestack_isdnl2(st);
 			break;
 		case (ISDN_PROTO_L2_HDLC):
 		case (ISDN_PROTO_L2_TRANS):
+//		case (ISDN_PROTO_L2_MODEM):
 			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
+*selectfreechannel(struct PStack *st)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp = st->lli.userdata;
+	int i;
 
-	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;
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		i=1;
+	else
+		i=0;
+	while (i<2) {
+		if (chanp->fi.state == ST_NULL)
+			return (chanp);
+		chanp++;
+		i++;
 	}
+	return (NULL);
 }
 
-static void
-cc_l2man(struct PStack *st, int pr, void *arg)
+int
+is_activ(struct PStack *st)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp = st->lli.userdata;
+	int i;
 
-	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;
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		i=1;
+	else
+		i=0;
+	while (i<2) {
+		if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+			return (1);
+		chanp++;
+		i++;
 	}
+	return (0);
 }
 
 static void
-dcc_l1man(struct PStack *st, int pr, void *arg)
+dchan_l3l4(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct l3_process *pc = arg;
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp;
+	int event;
 
 	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);
+		case (DL_ESTABLISH | INDICATION):
+			event = EV_DLEST;
 			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);
+		case (DL_RELEASE | INDICATION):
+			event = EV_DLRL;
 			break;
-		case (DL_RELEASE):
-			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+		default:
+			event = -1;
 			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);
-}
+	if (event >= 0) {
+		int i;
 
-static void
-ll_handler(struct PStack *st, int pr, void *arg)
-{
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
-	char tmp[64], tm[32];
+		chanp = st->lli.userdata;
+		if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+			i = 1;
+		else
+			i = 0;
+		while (i < 2) {
+			FsmEvent(&chanp->fi, event, NULL);
+			chanp++;
+			i++;
+		}
+		return;
+	} else if (pr == (CC_SETUP | INDICATION)) {
+		if (!(chanp = selectfreechannel(pc->st))) {
+			pc->st->lli.l4l3(pc->st, CC_DLRL | REQUEST, pc);
+		} else {
+			chanp->proc = pc;
+			pc->chan = chanp;
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+		}
+		return;
+	}
+	if (!(chanp = pc->chan))
+		return;
 
 	switch (pr) {
-		case (CC_DISCONNECT_IND):
+		case (CC_DISCONNECT | INDICATION):
 			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
 			break;
-		case (CC_RELEASE_CNF):
+		case (CC_RELEASE | CONFIRM):
 			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
 			break;
-		case (CC_SETUP_IND):
-			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+		case (CC_SUSPEND | CONFIRM):
+			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+			break;
+		case (CC_RESUME | CONFIRM):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
 			break;
-		case (CC_RELEASE_IND):
+		case (CC_RESUME_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+			break;
+		case (CC_RELEASE | INDICATION):
 			FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
 			break;
-		case (CC_SETUP_COMPLETE_IND):
+		case (CC_SETUP_COMPL | INDICATION):
 			FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
 			break;
-		case (CC_SETUP_CNF):
+		case (CC_SETUP | CONFIRM):
 			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
 			break;
-		case (CC_INFO_CHARGE):
+		case (CC_CHARGE | INDICATION):
 			FsmEvent(&chanp->fi, EV_CINF, NULL);
 			break;
-		case (CC_NOSETUP_RSP_ERR):
+		case (CC_NOSETUP_RSP):
 			FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
 			break;
 		case (CC_SETUP_ERR):
@@ -1386,130 +1307,97 @@
 		case (CC_RELEASE_ERR):
 			FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL);
 			break;
+		case (CC_PROCEEDING | INDICATION):
+		case (CC_ALERTING | INDICATION):
+			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);
+			if (chanp->debug & 0x800) {
+				HiSax_putstatus(chanp->cs, "Ch",
+					"%d L3->L4 unknown primitiv %x",
+					chanp->chan, pr);
 			}
 	}
 }
 
 static void
-init_is(struct Channel *chanp, unsigned int ces)
+init_d_st(struct Channel *chanp)
 {
-	struct PStack *st = &chanp->is;
-	struct IsdnCardState *sp = chanp->sp;
-	char tmp[128];
+	struct PStack *st = chanp->d_st;
+	struct IsdnCardState *cs = chanp->cs;
+	char tmp[16];
 
-	setstack_HiSax(st, sp);
+	HiSax_addlist(cs, st);
+	setstack_HiSax(st, cs);
 	st->l2.sap = 0;
-	st->l2.tei = 255;
-	st->l2.ces = ces;
-	st->l2.extended = !0;
-	st->l2.laptype = LAPD;
+	st->l2.tei = -1;
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_MOD128, &st->l2.flag);
+	test_and_set_bit(FLG_LAPD, &st->l2.flag);
+	test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.maxlen = MAX_DFRAME_LEN;
 	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);
+	st->l2.T200 = 1000;	/* 1000 milliseconds  */
+	st->l2.N200 = 3;	/* try 3 times        */
+	st->l2.T203 = 10000;	/* 10000 milliseconds */
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		sprintf(tmp, "DCh%d Q.921 ", chanp->chan);
+	else
+		sprintf(tmp, "DCh Q.921 ");
 	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);
+	setstack_l3dc(st, chanp);
+	st->lli.userdata = chanp;
+	st->lli.l2writewakeup = NULL;
+	st->l3.l3l4 = dchan_l3l4;
 }
 
 static void
-callc_debug(struct FsmInst *fi, char *s)
+callc_debug(struct FsmInst *fi, char *fmt, ...)
 {
-	char str[80], tm[32];
+	va_list args;
 	struct Channel *chanp = fi->userdata;
+	char tmp[16];
 
-	jiftime(tm, jiffies);
-	sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
-	HiSax_putstatus(chanp->sp, str);
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d callc ", chanp->chan);
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
 }
 
 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);
+dummy_pstack(struct PStack *st, int pr, void *arg) {
+	printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg);
 }
 
 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);
+init_PStack(struct PStack **stp) {
+	*stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+	(*stp)->next = NULL;
+	(*stp)->l1.l1l2 = dummy_pstack;
+	(*stp)->l1.l1hw = dummy_pstack;
+	(*stp)->l1.l1tei = dummy_pstack;
+	(*stp)->l2.l2tei = dummy_pstack;
+	(*stp)->l2.l2l1 = dummy_pstack;
+	(*stp)->l2.l2l3 = dummy_pstack;
+	(*stp)->l3.l3l2 = dummy_pstack;
+	(*stp)->l3.l3l4 = dummy_pstack;
+	(*stp)->lli.l4l3 = dummy_pstack;
+	(*stp)->ma.layer = dummy_pstack;
 }
 
 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)
+init_chan(int chan, struct IsdnCardState *csta)
 {
 	struct Channel *chanp = csta->channel + chan;
 
-	chanp->sp = csta;
-	chanp->hscx = hscx;
+	chanp->cs = csta;
+	chanp->bcs = csta->bcs + chan;
 	chanp->chan = chan;
 	chanp->incoming = 0;
 	chanp->debug = 0;
 	chanp->Flags = 0;
 	chanp->leased = 0;
-	chanp->impair = 0;
-	init_is(chanp, ces);
-
+	init_PStack(&chanp->b_st);
+	chanp->b_st->l1.delay = DEFAULT_B_DELAY;
 	chanp->fi.fsm = &callcfsm;
 	chanp->fi.state = ST_NULL;
 	chanp->fi.debug = 0;
@@ -1517,58 +1405,46 @@
 	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;
+	if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+		init_PStack(&chanp->d_st);
+		if (chan)
+			csta->channel->d_st->next = chanp->d_st;
+		chanp->d_st->next = NULL;
+		init_d_st(chanp);
+	} else {
+		chanp->d_st = csta->channel->d_st;
+	}
 	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++);
+	init_chan(0, csta);
+	init_chan(1, csta);
 	printk(KERN_INFO "HiSax: 2 channels added\n");
+	if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
+		printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+		test_and_set_bit(FLG_START_D, &csta->channel->Flags);
+		csta->channel->d_st->lli.l4l3(csta->channel->d_st,
+			DL_ESTABLISH | REQUEST, NULL);
+	}
 	return (2);
 }
 
 static void
-release_is(struct Channel *chanp)
+release_d_st(struct Channel *chanp)
 {
-	struct PStack *st = &chanp->is;
+	struct PStack *st = chanp->d_st;
 
+	if (!st)
+		return;
 	releasestack_isdnl2(st);
 	releasestack_isdnl3(st);
 	HiSax_rmlist(st->l1.hardware, st);
+	kfree(st);
+	chanp->d_st = NULL;
 }
 
 void
@@ -1579,32 +1455,46 @@
 	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);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
+			release_d_st(csta->channel + i);
+		if (csta->channel[i].b_st) {
+			if (test_and_clear_bit(FLG_START_B, &csta->channel[i].Flags))
+				release_b_st(csta->channel + i);
+			kfree(csta->channel[i].b_st);
+			csta->channel[i].b_st = NULL;
+		} else
+			printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+			release_d_st(csta->channel + i);
+		} else
+			csta->channel[i].d_st = NULL;
 	}
 }
 
 static void
 lldata_handler(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
 	struct sk_buff *skb = arg;
 
 	switch (pr) {
-		case (DL_DATA):
+		case (DL_DATA  | INDICATION):
 			if (chanp->data_open)
-				chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
 			else {
-				SET_SKB_FREE(skb);
 				dev_kfree_skb(skb, FREE_READ);
 			}
 			break;
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
 		default:
-			printk(KERN_WARNING "lldata_handler unknown primitive %d\n",
+			printk(KERN_WARNING "lldata_handler unknown primitive %x\n",
 			       pr);
 			break;
 	}
@@ -1613,99 +1503,167 @@
 static void
 lltrans_handler(struct PStack *st, int pr, void *arg)
 {
-	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
 	struct sk_buff *skb = arg;
 
 	switch (pr) {
-		case (PH_DATA):
+		case (PH_DATA | INDICATION):
 			if (chanp->data_open)
-				chanp->sp->iif.rcvcallb_skb(chanp->sp->myid, chanp->chan, skb);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
 			else {
-				SET_SKB_FREE(skb);
+				link_debug(chanp, 0, "channel not open");
 				dev_kfree_skb(skb, FREE_READ);
 			}
 			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
 		default:
-			printk(KERN_WARNING "lltrans_handler unknown primitive %d\n",
+			printk(KERN_WARNING "lltrans_handler unknown primitive %x\n",
 			       pr);
 			break;
 	}
 }
 
 static void
-ll_writewakeup(struct PStack *st)
+ll_writewakeup(struct PStack *st, int len)
 {
-	struct Channel *chanp = st->l4.userdata;
+	struct Channel *chanp = st->lli.userdata;
 	isdn_ctrl ic;
 
-	ic.driver = chanp->sp->myid;
+	ic.driver = chanp->cs->myid;
 	ic.command = ISDN_STAT_BSENT;
 	ic.arg = chanp->chan;
-	chanp->sp->iif.statcallb(&ic);
+//	ic.parm.length = len;
+	chanp->cs->iif.statcallb(&ic);
 }
 
 static int
-init_ds(struct Channel *chanp, int incoming)
+init_b_st(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))
+	struct PStack *st = chanp->b_st;
+	struct IsdnCardState *cs = chanp->cs;
+	char tmp[16];
+
+	st->l1.hardware = cs;
+	if (chanp->leased)
+		st->l1.bc = chanp->chan & 1;
+	else
+		st->l1.bc = chanp->proc->para.bchannel - 1;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+		case (ISDN_PROTO_L2_HDLC):
+			st->l1.mode = L1_MODE_HDLC;
+			break;
+		case (ISDN_PROTO_L2_TRANS):
+			st->l1.mode = L1_MODE_TRANS;
+			break;
+#if 0
+		case (ISDN_PROTO_L2_MODEM):
+			st->l1.mode = L1_MODE_MODEM;
+			break;
+#endif
+	}
+	if (chanp->bcs->BC_SetStack(st, chanp->bcs))
 		return (-1);
-
-	st->l2.extended = 0;
-	st->l2.laptype = LAPB;
-	st->l2.orig = !incoming;
-	st->l2.t200 = 1000;	/* 1000 milliseconds */
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_LAPB, &st->l2.flag);
+	st->l2.maxlen = MAX_DATA_SIZE;
+	if (!incoming)
+		test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.T200 = 1000;	/* 1000 milliseconds */
 	st->l2.window = 7;
-	st->l2.n200 = 4;	/* try 4 times       */
-	st->l2.t203 = 5000;	/* 5000 milliseconds */
-
+	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);
+			sprintf(tmp, "Ch%d X.75", chanp->chan);
 			setstack_isdnl2(st, tmp);
+			setstack_l3bc(st, chanp);
 			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->lli.userdata = chanp;
+			st->lli.l1writewakeup = NULL;
+			st->lli.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):
+//		case (ISDN_PROTO_L2_MODEM):
 			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;
+			st->lli.userdata = chanp;
+			st->lli.l1writewakeup = ll_writewakeup;
+			setstack_transl2(st);
+			setstack_l3bc(st, chanp);
 			break;
 	}
 	return (0);
 }
 
 static void
+leased_l4l3(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb, FREE_READ);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			break;
+		default:
+			printk(KERN_WARNING "transd_l4l3 unknown primitive %x\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+leased_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+	int i,event = EV_DLRL;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb, FREE_READ);
+			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			event = EV_DLEST;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags))
+				i = 1;
+			else
+				i = 0;
+			while (i < 2) {
+				FsmEvent(&chanp->fi, event, NULL);
+				chanp++;
+				i++;
+			}
+			break;
+		default:
+			printk(KERN_WARNING
+				"transd_l1l2 unknown primitive %x\n", pr);
+			break;
+	}
+}
+
+static void
 channel_report(struct Channel *chanp)
 {
 }
@@ -1719,25 +1677,86 @@
 	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;
+		chanp[i].d_st->l2.l2m.debug = debugflags & 8;
+		chanp[i].b_st->l2.l2m.debug = debugflags & 0x10;
+		chanp[i].d_st->l2.debug = debugflags & 0x20;
+		chanp[i].b_st->l2.debug = debugflags & 0x40;
+		chanp[i].d_st->l3.l3m.debug = debugflags & 0x80;
+		chanp[i].b_st->l3.l3m.debug = debugflags & 0x100;
+		chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200;
+		chanp[i].b_st->ma.debug = debugflags & 0x200;
+		chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000;
+		chanp[i].b_st->l1.l1m.debug = debugflags & 0x2000;
 	}
-	csta->dlogflag = debugflags & 4;
-	csta->teistack->l2.l2m.debug = debugflags & 512;
+	if (debugflags & 4)
+		csta->debug |= DEB_DLOG_HEX;
+	else
+		csta->debug &= ~DEB_DLOG_HEX;
 }
 
+#if 0
+static char tmpbuf[256];
+
+static void
+capi_debug(struct Channel *chanp, capi_msg *cm)
+{
+	char *t = tmpbuf;
+
+	t += sprintf(tmpbuf, "%d CAPIMSG", chanp->chan);
+	t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length);
+	t--;
+	*t= 0;
+	HiSax_putstatus(chanp->cs, "Ch", "%d CAPIMSG %s", chanp->chan, tmpbuf);
+}
+
+void
+lli_got_fac_req(struct Channel *chanp, capi_msg *cm) {
+	if ((cm->para[0] != 3) || (cm->para[1] != 0))
+		return;
+	if (cm->para[2]<3)
+		return;
+	if (cm->para[4] != 0)
+		return;
+	switch(cm->para[3]) {
+		case 4: /* Suspend */
+			if (cm->para[5]) {
+				strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+				FsmEvent(&chanp->fi, EV_SUSPEND, cm);
+			}
+			break;
+		case 5: /* Resume */
+			if (cm->para[5]) {
+				strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+				if (chanp->fi.state == ST_NULL) {
+					FsmEvent(&chanp->fi, EV_RESUME, cm);
+				} else {
+					FsmDelTimer(&chanp->dial_timer, 72);
+					FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73);
+				}
+			}
+			break;
+	}
+}
+
+void
+lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) {
+	if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) ||
+		(cs->typ == ISDN_CTYPE_ELSA_PCI)) {
+		if (cs->hw.elsa.MFlag) {
+			cs->cardmsg(cs, CARD_AUX_IND, cm->para);
+		}
+	}
+}
+#endif
+
 int
 HiSax_command(isdn_ctrl * ic)
 {
 	struct IsdnCardState *csta = hisax_findcard(ic->driver);
 	struct Channel *chanp;
-	char tmp[128];
 	int i;
-	unsigned int num;
+	u_int num;
+	u_long adr;
 
 	if (!csta) {
 		printk(KERN_ERR
@@ -1745,32 +1764,35 @@
 		       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);
-			}
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "SETL2 card %d %ld",
+					csta->cardnr + 1, ic->arg >> 8);
 			chanp->l2_protocol = ic->arg >> 8;
 			break;
+		case (ISDN_CMD_SETL3):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "SETL3 card %d %ld",
+					csta->cardnr + 1, ic->arg >> 8);
+			chanp->l3_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)",
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "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';
+					ic->parm.setup.si1, ic->parm.setup.si2);
+			chanp->setup = ic->parm.setup;
+			if (!strcmp(chanp->setup.eazmsn, "0"))
+				chanp->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) {
@@ -1783,57 +1805,56 @@
 		case (ISDN_CMD_ACCEPTB):
 			chanp = csta->channel + ic->arg;
 			if (chanp->debug & 1)
-				link_debug(chanp, "ACCEPTB", 1);
+				link_debug(chanp, 1, "ACCEPTB");
 			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);
+				link_debug(chanp, 1, "ACCEPTD");
 			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);
+				link_debug(chanp, 1, "HANGUP");
 			FsmEvent(&chanp->fi, EV_HANGUP, NULL);
 			break;
-		case (ISDN_CMD_SUSPEND):
+#if 0
+		case (CAPI_PUT_MESSAGE):
 			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);
+			if (chanp->debug & 1)
+				capi_debug(chanp, &ic->parm.cmsg);
+			if (ic->parm.cmsg.Length < 8)
+				break;
+			switch(ic->parm.cmsg.Command) {
+				case CAPI_FACILITY:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_fac_req(chanp, &ic->parm.cmsg);
+					break;
+				case CAPI_MANUFACTURER:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_manufacturer(chanp, csta, &ic->parm.cmsg);
+					break;
+				default:
+					break;
 			}
-			FsmEvent(&chanp->fi, EV_RESUME, ic);
 			break;
+#endif
 		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);
-			}
+			if (csta->channel[0].debug & 0x400)
+				HiSax_putstatus(csta, "   LOCK ", "modcnt %lx",
+					MOD_USE_COUNT);
 #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);
-			}
+			if (csta->channel[0].debug & 0x400)
+				HiSax_putstatus(csta, " UNLOCK ", "modcnt %lx",
+					MOD_USE_COUNT);
 #endif				/* MODULE */
 			break;
 		case (ISDN_CMD_IOCTL):
@@ -1846,22 +1867,19 @@
 				case (1):
 					num = *(unsigned int *) ic->parm.num;
 					distr_debug(csta, num);
-					sprintf(tmp, "debugging flags card %d set to %x\n",
+					printk(KERN_DEBUG "HiSax: debugging flags card %d set to %x\n",
 						csta->cardnr + 1, num);
-					HiSax_putstatus(csta, tmp);
-					printk(KERN_DEBUG "HiSax: %s", tmp);
+					HiSax_putstatus(csta, "debugging flags ",
+						"card %d set to %x", csta->cardnr + 1, num);
 					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);
-					}
+					csta->channel[0].b_st->l1.delay = num;
+					csta->channel[1].b_st->l1.delay = num;
+					HiSax_putstatus(csta, "delay ", "card %d set to %d ms",
+						csta->cardnr + 1, num);
+					printk(KERN_DEBUG "HiSax: delay card %d set to %d ms\n",
+						csta->cardnr + 1, num);
 					break;
 				case (3):
 					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
@@ -1872,36 +1890,92 @@
 						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);
+					num = *(unsigned int *) ic->parm.num;
+					if ((num <1) || (num > 2)) {
+						HiSax_putstatus(csta, "Set LEASED ",
+							"wrong channel %d", num);
+						printk(KERN_WARNING "HiSax: Set LEASED wrong channel %d\n",
+							num);
+					} else {
+						num--;
+						chanp = csta->channel +num;
+						chanp->leased = 1;
+						HiSax_putstatus(csta, "Card",
+							"%d channel %d set leased mode\n",
+							csta->cardnr + 1, num + 1);
+						chanp->d_st->l1.l1l2 = leased_l1l2;
+						chanp->d_st->lli.l4l3 = leased_l4l3;
+						chanp->d_st->lli.l4l3(chanp->d_st,
+							DL_ESTABLISH | REQUEST, NULL);
+					}
+					break;
+				case (6):	/* set B-channel test loop */
+					num = *(unsigned int *) ic->parm.num;
+					if (csta->stlist)
+						csta->stlist->l2.l2l1(csta->stlist,
+							PH_TESTLOOP | REQUEST, (void *) (long)num);
+					break;
+				case (7):	/* set card in PTP mode */
+					num = *(unsigned int *) ic->parm.num;
+					if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+						printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n");
+					} else if (num) {
+						test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						csta->channel[0].d_st->l2.tei = 0;
+						HiSax_putstatus(csta, "set card ", "in PTP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
+						printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+						test_and_set_bit(FLG_START_D, &csta->channel[0].Flags);
+						test_and_set_bit(FLG_START_D, &csta->channel[1].Flags);
+						csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
+							DL_ESTABLISH | REQUEST, NULL);
+					} else {
+						test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						HiSax_putstatus(csta, "set card ", "in PTMP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTMP mode\n");
+					}
+					break;
+				case (8):	/* set card in FIXED TEI mode */
+					num = *(unsigned int *) ic->parm.num;
+					chanp = csta->channel + (num & 1);
+					num = num >>1;
+					test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+					chanp->d_st->l2.tei = num;
+					HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
+					printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
+						num);
+					break;
+				case (9): /* load firmware */
+					memcpy(&adr, ic->parm.num, sizeof(ulong));
+					csta->cardmsg(csta, CARD_LOAD_FIRM,
+						(void *) adr);
 					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):
+					num = csta->debug & DEB_DLOG_HEX;
 					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);
+					csta->debug |= num;
+					HiSax_putstatus(cards[0].cs, "l1 debugging ",
+						"flags card %d set to %x",
+						csta->cardnr + 1, csta->debug);
+					printk(KERN_DEBUG "HiSax: l1 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, csta->debug);
 					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->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+					csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+					HiSax_putstatus(cards[0].cs, "l3 debugging ",
+						"flags card %d set to %x\n", csta->cardnr + 1,
+						*(unsigned int *) ic->parm.num);
+					printk(KERN_DEBUG "HiSax: 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",
@@ -1925,7 +1999,6 @@
 	int len = skb->len;
 	unsigned long flags;
 	struct sk_buff *nskb;
-	char tmp[64];
 
 	if (!csta) {
 		printk(KERN_ERR
@@ -1933,38 +2006,37 @@
 		return -ENODEV;
 	}
 	chanp = csta->channel + chan;
-	st = &chanp->ds;
+	st = chanp->b_st;
 	if (!chanp->data_open) {
-		link_debug(chanp, "writebuf: channel not open", 1);
+		link_debug(chanp, 1, "writebuf: channel not open");
 		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);
+		link_debug(chanp, 1, "writebuf: packet too large (%d bytes)", len);
+		printk(KERN_WARNING "HiSax_writebuf: packet too large (%d bytes) !\n",
+			len);
 		return -EINVAL;
 	}
 	if (len) {
-		if ((len + csta->hs[chanp->hscx].tx_cnt) > MAX_DATA_MEM) {
+		if ((len + chanp->bcs->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);
-			}
+			if (chanp->debug & 0x800)
+				link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len);
 			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);
+//			if (!ack)
+//				nskb->pkt_type = PACKET_NOACK;
+			if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
+				st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
+			else {
+				chanp->bcs->tx_cnt += len;
+				st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
 			}
 			dev_kfree_skb(skb, FREE_WRITE);
 		} else
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/cert.c linux/drivers/isdn/hisax/cert.c
--- v2.0.35/linux/drivers/isdn/hisax/cert.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/cert.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,52 @@
+/* $Id: cert.c,v 1.2.2.1 1998/11/03 21:46:37 keil Exp $
+
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
+ * $Log: cert.c,v $
+ * Revision 1.2.2.1  1998/11/03 21:46:37  keil
+ * first version
+ *
+ *
+ */
+ 
+#include <linux/kernel.h>
+
+int
+certification_check(int output) {
+
+#ifdef CERTIFICATION
+#if CERTIFICATION == 0
+	if (output) {
+		printk(KERN_INFO "HiSax: Approval certification valid\n");
+		printk(KERN_INFO "HiSax: Approved with ELSA Quickstep series cards\n");
+		printk(KERN_INFO "HiSax: Approval registration numbers:\n");
+		printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n");
+		printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n");
+	}
+	return(0);
+#endif
+#if CERTIFICATION == 1
+	if (output) {
+		printk(KERN_INFO "HiSax: Approval certification failed because of\n");
+		printk(KERN_INFO "HiSax: unauthorized source code changes\n");
+	}
+	return(1);
+#endif
+#if CERTIFICATION == 127
+	if (output) {
+		printk(KERN_INFO "HiSax: Approval certification not possible\n");
+		printk(KERN_INFO "HiSax: because \"md5sum\" is not available\n");
+	}
+	return(2);
+#endif
+#else
+	if (output) {
+		printk(KERN_INFO "HiSax: Certification not verified\n");
+	}
+	return(3);
+#endif
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.0.35/linux/drivers/isdn/hisax/config.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/config.c	Sun Nov 15 10:32:58 1998
@@ -1,58 +1,97 @@
-/* $Id: config.c,v 1.15 1997/04/06 22:57:24 keil Exp $
+/* $Id: config.c,v 1.15.2.24 1998/11/11 13:37:53 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.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.15.2.24  1998/11/11 13:37:53  keil
+ * fix typo
  *
- * Revision 1.14  1997/03/25 23:11:22  keil
- * US NI-1 protocol
+ * Revision 1.15.2.23  1998/11/11 11:04:36  keil
+ * update version
  *
- * Revision 1.13  1997/03/23 21:45:49  keil
- * Add support for ELSA PCMCIA
+ * Revision 1.15.2.22  1998/11/08 13:00:56  niemann
+ * Added doc for Sedlbauer ISDN cards,
+ * added info for downloading firmware (Sedlbauer speed fax+)
  *
- * Revision 1.12  1997/03/11 21:01:43  keil
- * nzproto is only used with modules
+ * Revision 1.15.2.21  1998/11/05 21:13:46  keil
+ * minor fixes
  *
- * Revision 1.11  1997/02/14 12:23:12  fritz
- * Added support for new insmod parameter handling.
+ * Revision 1.15.2.20  1998/11/04 11:50:32  fritz
+ * Make compile with modversions enabled.
  *
- * Revision 1.10  1997/02/14 09:22:09  keil
- * Final 2.0 version
+ * Revision 1.15.2.19  1998/11/03 00:06:05  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.9  1997/02/10 11:45:09  fritz
- * More changes for Kernel 2.1.X compatibility.
+ * Revision 1.15.2.18  1998/10/13 10:27:26  keil
+ * New cards, minor fixes
  *
- * Revision 1.8  1997/02/09 00:28:05  keil
- * new interface handling, one interface per card
- * default protocol now works again
+ * Revision 1.15.2.17  1998/10/11 19:31:31  niemann
+ * Fixed problems with CONFIG_MODVERSIONS for sedlbauer cards
  *
- * Revision 1.7  1997/01/27 15:56:57  keil
- * Teles PCMCIA ITK ix1 micro added
+ * Revision 1.15.2.16  1998/09/27 13:05:48  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
- * Revision 1.6  1997/01/21 22:17:56  keil
- * new module load syntax
+ * Revision 1.15.2.15  1998/09/12 18:43:56  niemann
+ * Added new card: Sedlbauer ISDN-Controller PC/104
  *
- * Revision 1.5  1997/01/09 18:28:20  keil
- * cosmetic cleanups
+ * Revision 1.15.2.14  1998/08/25 14:01:27  calle
+ * Ported driver for AVM Fritz!Card PCI from the 2.1 tree.
+ * I could not test it.
  *
- * Revision 1.4  1996/11/05 19:35:17  keil
- * using config.h; some spelling fixes
+ * Revision 1.15.2.13  1998/07/30 20:51:24  niemann
+ * Fixed Sedlbauer Speed Card PCMCIA missing isdnl3new
  *
- * Revision 1.3  1996/10/23 17:23:28  keil
- * default config changes
+ * Revision 1.15.2.12  1998/07/15 14:43:29  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
  *
- * Revision 1.2  1996/10/23 11:58:48  fritz
- * Changed default setup to reflect user's selection of supported
- * cards/protocols.
+ * Revision 1.15.2.11  1998/05/27 18:05:07  keil
+ * HiSax 3.0
  *
- * Revision 1.1  1996/10/13 20:04:51  keil
- * Initial revision
+ * Revision 1.15.2.10  1998/04/11 18:43:13  keil
+ * New cards
  *
+ * Revision 1.15.2.9  1998/03/07 23:15:12  tsbogend
+ * made HiSax working on Linux/Alpha
  *
+ * Revision 1.15.2.8  1998/02/11 19:21:37  keil
+ * fix typo
+ *
+ * Revision 1.15.2.7  1998/02/11 14:23:08  keil
+ * support for Dr Neuhaus Niccy PnP and PCI
+ *
+ * Revision 1.15.2.6  1998/02/09 11:21:19  keil
+ * Sedlbauer PCMCIA support from Marcus Niemann
+ *
+ * Revision 1.15.2.5  1998/01/27 23:28:48  keil
+ * v2.8
+ *
+ * Revision 1.15.2.4  1998/01/27 22:33:53  keil
+ * dynalink ----> asuscom
+ *
+ * Revision 1.15.2.3  1998/01/11 22:55:15  keil
+ * 16.3c support
+ *
+ * Revision 1.15.2.2  1997/11/15 18:55:46  keil
+ * New init, new cards
+ *
+ * Revision 1.15.2.1  1997/10/17 22:13:40  keil
+ * update to last hisax version
+ *
+ * Revision 2.2  1997/09/11 17:24:46  keil
+ * Add new cards
+ *
+ * Revision 2.1  1997/07/27 21:41:35  keil
+ * version change
+ *
+ * Revision 2.0  1997/06/26 11:06:28  keil
+ * New card and L1 interface.
+ * Eicon.Diehl Diva and Dynalink IS64PH support
+ *
+ * old changes removed /KKe
  *
  */
 #include <linux/types.h>
@@ -60,6 +99,13 @@
 #include <linux/timer.h>
 #include <linux/config.h>
 #include "hisax.h"
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#define kstat_irqs( PAR ) kstat.interrupts[PAR]
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#define HISAX_STATUS_BUFSIZE 4096
+#define INCLUDE_INLINE_FUNCS
 
 /*
  * This structure array contains one entry per card. An entry looks
@@ -68,55 +114,202 @@
  * { 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
- *
+ *    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
+ *    8 Teles PCMCIA     p0=irq p1=iobase
+ *    9 ITK ix1-micro    p0=irq p1=iobase
+ *   10 ELSA PCMCIA      p0=irq p1=iobase
+ *   11 Eicon.Diehl Diva p0=irq p1=iobase
+ *   12 Asuscom ISDNLink p0=irq p1=iobase
+ *   13 Teleint          p0=irq p1=iobase
+ *   14 Teles 16.3c      p0=irq p1=iobase
+ *   15 Sedlbauer speed  p0=irq p1=iobase
+ *   15 Sedlbauer PC/104	p0=irq p1=iobase
+ *   15 Sedlbauer speed pci	no parameter
+ *   16 USR Sportster internal  p0=irq  p1=iobase
+ *   17 MIC card                p0=irq  p1=iobase
+ *   18 ELSA Quickstep 1000PCI  no parameter
+ *   19 Compaq ISDN S0 ISA card p0=irq  p1=IO0 (HSCX)  p2=IO1 (ISAC) p3=IO2
+ *   20 Travers Technologies NETjet PCI card
+ *   21 TELES PCI               no parameter
+ *   22 Sedlbauer Speed Star    p0=irq p1=iobase
+ *   23 reserved
+ *   24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only)
+ *   25 Teles S0Box             p0=irq p1=iobase (from isapnp setup)
+ *   26 AVM A1 PCMCIA (Fritz)   p0=irq p1=iobase
+ *   27 AVM PnP/PCI 		p0=irq p1=iobase (PCI no parameter)
+ *   28 Sedlbauer Speed Fax+ 	p0=irq p1=iobase (from isapnp setup)
  *
  * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
  *
  *
  */
 
-#ifdef CONFIG_HISAX_ELSA_PCC
+const char *CardType[] =
+{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP",
+ "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2",
+ "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c",
+ "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
+ "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
+ "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI",
+ "Sedlbauer Speed Fax +"
+};
+
+#ifdef CONFIG_HISAX_ELSA
 #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}
+#define DEFAULT_CFG {0,0,0,0}
+int elsa_init_pcmcia(void*, int, int*, int);
+static struct symbol_table hisax_syms_elsa = {
+#include <linux/symtab_begin.h>
+	X(elsa_init_pcmcia),
+#include <linux/symtab_end.h>
+};
+void register_elsa_symbols(void) {
+	register_symtab(&hisax_syms_elsa);
+}
 #endif
 #ifdef CONFIG_HISAX_AVM_A1
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_A1
-#define DEFAULT_CFG {10,0x340,0}
+#define DEFAULT_CFG {10,0x340,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA
+#define DEFAULT_CFG {11,0x170,0,0}
+int avm_a1_init_pcmcia(void*, int, int*, int);
+void HiSax_closecard(int cardnr);
+static struct symbol_table hisax_syms_avm_a1= {
+#include <linux/symtab_begin.h>
+	X(avm_a1_init_pcmcia),
+	X(HiSax_closecard),
+#include <linux/symtab_end.h>
+};
+void register_avm_a1_symbols(void) {
+	register_symtab(&hisax_syms_avm_a1);
+}
+#endif
+#ifdef CONFIG_HISAX_FRITZPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_FRITZPCI
+#define DEFAULT_CFG {0,0,0,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}
+#define DEFAULT_CFG {15,0x180,0,0}
+#endif
+#ifdef CONFIG_HISAX_S0BOX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_S0BOX
+#define DEFAULT_CFG {7,0x378,0,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}
+#define DEFAULT_CFG {15,0xd0000,0xd80,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELESPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELESPCI
+#define DEFAULT_CFG {0,0,0,0}
 #endif
 
 #ifdef CONFIG_HISAX_IX1MICROR2
 #undef DEFAULT_CARD
 #undef DEFAULT_CFG
 #define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2
-#define DEFAULT_CFG {5,0x390,0}
+#define DEFAULT_CFG {5,0x390,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_DIEHLDIVA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ASUSCOM
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM
+#define DEFAULT_CFG {5,0x200,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELEINT
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELEINT
+#define DEFAULT_CFG {5,0x300,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SEDLBAUER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER
+#define DEFAULT_CFG {11,0x270,0,0}
+int sedl_init_pcmcia(void*, int, int*, int);
+static struct symbol_table hisax_syms_sedl= {
+#include <linux/symtab_begin.h>
+	X(sedl_init_pcmcia),
+#include <linux/symtab_end.h>
+};
+#endif
+
+#ifdef CONFIG_HISAX_SPORTSTER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER
+#define DEFAULT_CFG {7,0x268,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_MIC
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_MIC
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELES3C
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELES3C
+#define DEFAULT_CFG {5,0x500,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AMD7930
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_AMD7930
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NICCY
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NICCY
+#define DEFAULT_CFG {0,0x0,0,0}
 #endif
 
 #ifdef CONFIG_HISAX_1TR6
@@ -144,13 +337,13 @@
 #endif
 
 #define FIRST_CARD { \
-  DEFAULT_CARD, \
-  DEFAULT_PROTO, \
-  DEFAULT_CFG, \
-  NULL, \
+	DEFAULT_CARD, \
+	DEFAULT_PROTO, \
+	DEFAULT_CFG, \
+	NULL, \
 }
 
-#define EMPTY_CARD	{0, DEFAULT_PROTO, {0, 0, 0}, NULL}
+#define EMPTY_CARD	{0, DEFAULT_PROTO, {0, 0, 0, 0}, NULL}
 
 struct IsdnCard cards[] =
 {
@@ -162,65 +355,65 @@
 	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" \
+static char HiSaxID[64] HISAX_INITDATA = "\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;
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+char *HiSax_id HISAX_INITDATA = 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;
+static int type[] HISAX_INITDATA =
+{0, 0, 0, 0, 0, 0, 0, 0};
+static int protocol[] HISAX_INITDATA =
+{0, 0, 0, 0, 0, 0, 0, 0};
+static int io[] HISAX_INITDATA =
+{0, 0, 0, 0, 0, 0, 0, 0};
+#undef IO0_IO1
+#ifdef CONFIG_HISAX_16_3
+#define IO0_IO1
+#endif
+#ifdef CONFIG_HISAX_NICCY
+#undef IO0_IO1
+#define IO0_IO1
+#endif
+#ifdef IO0_IO1
+static int io0[] HISAX_INITDATA =
+{0, 0, 0, 0, 0, 0, 0, 0};
+static int io1[] HISAX_INITDATA =
+{0, 0, 0, 0, 0, 0, 0, 0};
+#endif
+static int irq[] HISAX_INITDATA =
+{0, 0, 0, 0, 0, 0, 0, 0};
+static int mem[] HISAX_INITDATA =
+{0, 0, 0, 0, 0, 0, 0, 0};
+static char *id HISAX_INITDATA = 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(type, "1-8i");
+MODULE_PARM(protocol, "1-8i");
+MODULE_PARM(io, "1-8i");
+MODULE_PARM(irq, "1-8i");
+MODULE_PARM(mem, "1-8i");
 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
+MODULE_PARM(io0, "1-8i");
+MODULE_PARM(io1, "1-8i");
+#endif /* CONFIG_HISAX_16_3 */
+#endif /* (LINUX_VERSION_CODE > 0x020111) */
+#endif /* MODULE */
 
-#endif
+int nrcards;
 
 extern char *l1_revision;
 extern char *l2_revision;
 extern char *l3_revision;
-extern char *l4_revision;
+extern char *lli_revision;
 extern char *tei_revision;
 
-char *
-HiSax_getrev(const char *revision)
+HISAX_INITFUNC(char *
+HiSax_getrev(const char *revision))
 {
 	char *rev;
 	char *p;
@@ -234,7 +427,29 @@
 	return rev;
 }
 
-int nrcards;
+HISAX_INITFUNC(void
+HiSaxVersion(void))
+{
+	char tmp[64];
+
+	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
+#ifdef MODULE
+	printk(KERN_INFO "HiSax: Version 3.1 (module)\n");
+#else
+	printk(KERN_INFO "HiSax: Version 3.1 (kernel)\n");
+#endif
+	strcpy(tmp, l1_revision);
+	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, l2_revision);
+	printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, tei_revision);
+	printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, l3_revision);
+	printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, lli_revision);
+	printk(KERN_INFO "HiSax: LinkLayer Revision %s\n", HiSax_getrev(tmp));
+	certification_check(1);
+}
 
 void
 HiSax_mod_dec_use_count(void)
@@ -251,15 +466,15 @@
 #ifdef MODULE
 #define HiSax_init init_module
 #else
-void
-HiSax_setup(char *str, int *ints)
+__initfunc(void
+HiSax_setup(char *str, int *ints))
 {
 	int i, j, argc;
 
 	argc = ints[0];
 	i = 0;
 	j = 1;
-	while (argc && (i < 16)) {
+	while (argc && (i < HISAX_MAX_CARDS)) {
 		if (argc) {
 			cards[i].typ = ints[j];
 			j++;
@@ -297,35 +512,783 @@
 }
 #endif
 
+#if CARD_TELES0
+extern int setup_teles0(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3
+extern int setup_teles3(struct IsdnCard *card);
+#endif
+
+#if CARD_S0BOX
+extern int setup_s0box(struct IsdnCard *card);
+#endif
+
+#if CARD_TELESPCI
+extern int setup_telespci(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1
+extern int setup_avm_a1(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1_PCMCIA
+extern int setup_avm_a1_pcmcia(struct IsdnCard *card);
+#endif
+
+#if CARD_FRITZPCI
+extern int setup_avm_pcipnp(struct IsdnCard *card);
+#endif
+
+#if CARD_ELSA
+extern int setup_elsa(struct IsdnCard *card);
+#endif
+
+#if CARD_IX1MICROR2
+extern int setup_ix1micro(struct IsdnCard *card);
+#endif
+
+#if CARD_DIEHLDIVA
+extern	int  setup_diva(struct IsdnCard *card);
+#endif
+
+#if CARD_ASUSCOM
+extern int setup_asuscom(struct IsdnCard *card);
+#endif
+
+#if CARD_TELEINT
+extern int setup_TeleInt(struct IsdnCard *card);
+#endif
+
+#if CARD_SEDLBAUER
+extern int setup_sedlbauer(struct IsdnCard *card);
+#endif
+
+#if CARD_SPORTSTER
+extern int setup_sportster(struct IsdnCard *card);
+#endif
+
+#if CARD_MIC
+extern int setup_mic(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET
+extern int setup_netjet(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3C
+extern int setup_t163c(struct IsdnCard *card);
+#endif
+
+#if CARD_AMD7930
+extern int setup_amd7930(struct IsdnCard *card);
+#endif
+
+#if CARD_NICCY
+extern int setup_niccy(struct IsdnCard *card);
+#endif
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return (cards[i].cs);
+	return (NULL);
+}
+
 int
-HiSax_init(void)
+HiSax_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	int count;
+	u_char *p = buf;
+	struct IsdnCardState *cs = hisax_findcard(id);
+
+	if (cs) {
+		if (len > HISAX_STATUS_BUFSIZE) {
+			printk(KERN_WARNING "HiSax: status overflow readstat %d/%d",
+				len, HISAX_STATUS_BUFSIZE);
+			return -ENODEV;
+		}
+		count = cs->status_end - cs->status_read +1;
+		if (count >= len)
+			count = len;
+		if (user)
+			copy_to_user(p, cs->status_read, count);
+		else
+			memcpy(p, cs->status_read, count);
+		cs->status_read += count;
+		if (cs->status_read > cs->status_end)
+			cs->status_read = cs->status_buf;
+		p += count;
+		count = len - count;
+		if (count) {
+			if (user)
+				copy_to_user(p, cs->status_read, count);
+			else
+				memcpy(p, cs->status_read, count);
+			cs->status_read += count;
+		}
+		return len;
+	} else {
+		printk(KERN_ERR
+		 "HiSax: if_readstatus called with invalid driverId!\n");
+		return -ENODEV;
+	}
+}
+
+inline int
+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';
+	return(8);
+}
+
+static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
+
+void
+VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args)
+{
+/* if head == NULL the fmt contains the full info */
+
+	long flags;
+	int count, i;
+	u_char *p;
+	isdn_ctrl ic;
+	int len;
+
+	save_flags(flags);
+	cli();
+	p = tmpbuf;
+	if (head) {
+		p += jiftime(p, jiffies);
+		p += sprintf(p, " %s", head);
+		p += vsprintf(p, fmt, args);
+		*p++ = '\n';
+		*p = 0;
+		len = p - tmpbuf;
+		p = tmpbuf;
+	} else {
+		p = fmt;
+		len = strlen(fmt);
+	}
+	if (!cs) {
+		printk(KERN_WARNING "HiSax: No CardStatus for message %s", p);
+		restore_flags(flags);
+		return;
+	}
+	if (len > HISAX_STATUS_BUFSIZE) {
+		printk(KERN_WARNING "HiSax: status overflow %d/%d",
+			len, HISAX_STATUS_BUFSIZE);
+		restore_flags(flags);
+		return;
+	}
+	count = len;
+	i = cs->status_end - cs->status_write +1;
+	if (i >= len)
+		i = len;
+	len -= i;
+	memcpy(cs->status_write, p, i);
+	cs->status_write += i;
+	if (cs->status_write > cs->status_end)
+		cs->status_write = cs->status_buf;
+	p += i;
+	if (len) {
+		memcpy(cs->status_write, p, len);
+		cs->status_write += len;
+	}
+#ifdef KERNELSTACK_DEBUG
+	i = (ulong)&len - current->kernel_stack_page;
+	sprintf(tmpbuf, "kstack %s %lx use %ld\n", current->comm,
+		current->kernel_stack_page, i);
+	len = strlen(tmpbuf);
+	for (p = tmpbuf, i = len; i > 0; i--, p++) {
+		*cs->status_write++ = *p;
+		if (cs->status_write > cs->status_end)
+			cs->status_write = cs->status_buf;
+		count++;
+	}
+#endif
+	restore_flags(flags);
+	if (count) {
+		ic.command = ISDN_STAT_STAVAIL;
+		ic.driver = cs->myid;
+		ic.arg = count;
+		cs->iif.statcallb(&ic);
+	}
+}
+
+void
+HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(cs, head, fmt, args);
+	va_end(args);
+}
+
+int
+ll_run(struct IsdnCardState *cs)
+{
+	long flags;
+	isdn_ctrl ic;
+
+	save_flags(flags);
+	cli();
+	ic.driver = cs->myid;
+	ic.command = ISDN_STAT_RUN;
+	cs->iif.statcallb(&ic);
+	restore_flags(flags);
+	return 0;
+}
+
+void
+ll_stop(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_STOP;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	CallcFreeChan(cs);
+}
+
+static void
+ll_unload(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_UNLOAD;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	if (cs->status_buf)
+		kfree(cs->status_buf);
+	cs->status_read = NULL;
+	cs->status_write = NULL;
+	cs->status_end = NULL;
+	kfree(cs->dlog);
+}
+
+static void
+closecard(int cardnr)
+{
+	struct IsdnCardState *csta = cards[cardnr].cs;
+
+	if (csta->bcs->BC_Close != NULL) {
+		csta->bcs->BC_Close(csta->bcs + 1);
+		csta->bcs->BC_Close(csta->bcs);
+	}
+
+	if (csta->rcvbuf) {
+		kfree(csta->rcvbuf);
+		csta->rcvbuf = NULL;
+	}
+	discard_queue(&csta->rq);
+	discard_queue(&csta->sq);
+	if (csta->tx_skb) {
+		dev_kfree_skb(csta->tx_skb, FREE_WRITE);
+		csta->tx_skb = NULL;
+	}
+	if (csta->mon_rx) {
+		kfree(csta->mon_rx);
+		csta->mon_rx = NULL;
+	}
+	if (csta->mon_tx) {
+		kfree(csta->mon_tx);
+		csta->mon_tx = NULL;
+	}
+	csta->cardmsg(csta, CARD_RELEASE, NULL);
+	if (csta->dbusytimer.function != NULL)
+		del_timer(&csta->dbusytimer);
+	ll_unload(csta);
+}
+
+HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs))
+{
+	int irq_cnt, cnt = 3;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	irq_cnt = kstat_irqs(cs->irq);
+	printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq,
+		irq_cnt);
+	if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+			cs->irq);
+		restore_flags(flags);
+		return(1);
+	}
+	while (cnt) {
+		cs->cardmsg(cs, CARD_INIT, NULL);
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		/* Timeout 10ms */
+		current->timeout = jiffies + (10 * HZ) / 1000;
+		schedule();
+		restore_flags(flags);
+		printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
+			cs->irq, kstat_irqs(cs->irq));
+		if (kstat_irqs(cs->irq) == irq_cnt) {
+			printk(KERN_WARNING
+			       "%s: IRQ(%d) getting no interrupts during init %d\n",
+			       CardType[cs->typ], cs->irq, 4 - cnt);
+			if (cnt == 1) {
+				free_irq(cs->irq, cs);
+				return (2);
+			} else {
+				cs->cardmsg(cs, CARD_RESET, NULL);
+				cnt--;
+			}
+		} else {
+			cs->cardmsg(cs, CARD_TEST, NULL);
+			return(0);
+		}
+	}
+	restore_flags(flags);
+	return(3);
+}
+
+HISAX_INITFUNC(static int
+checkcard(int cardnr, char *id, int *busy_flag))
+{
+	long flags;
+	int ret = 0;
+	struct IsdnCard *card = cards + cardnr;
+	struct IsdnCardState *cs;
+
+	save_flags(flags);
+	cli();
+	if (!(cs = (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);
+	}
+	memset(cs, 0, sizeof(struct IsdnCardState));
+	card->cs = cs;
+	cs->cardnr = cardnr;
+	cs->debug = L1_DEB_WARN;
+	cs->HW_Flags = 0;
+	cs->busy_flag = busy_flag;
+#if TEI_PER_CARD
+#else
+	test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#endif
+	cs->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 (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for dlog(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for status_buf(card %d)\n",
+		       cardnr + 1);
+		kfree(cs->dlog);
+		restore_flags(flags);
+		return (0);
+	}
+	cs->stlist = NULL;
+	cs->mon_tx = NULL;
+	cs->mon_rx = NULL;
+	cs->status_read = cs->status_buf;
+	cs->status_write = cs->status_buf;
+	cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
+	cs->typ = card->typ;
+	strcpy(cs->iif.id, id);
+	cs->iif.channels = 2;
+	cs->iif.maxbufsize = MAX_DATA_SIZE;
+	cs->iif.hl_hdrlen = MAX_HEADER_LEN;
+	cs->iif.features =
+	    ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+//	    ISDN_FEATURE_L2_MODEM |
+	    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;
+
+	cs->iif.command = HiSax_command;
+	cs->iif.writecmd = NULL;
+	cs->iif.writebuf_skb = HiSax_writebuf_skb;
+	cs->iif.readstat = HiSax_readstatus;
+	register_isdn(&cs->iif);
+	cs->myid = cs->iif.channels;
+	printk(KERN_INFO
+	       "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", cs->iif.id, cs->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:
+		case ISDN_CTYPE_COMPAQ_ISA:
+			ret = setup_teles3(card);
+			break;
+#endif
+#if CARD_S0BOX
+		case ISDN_CTYPE_S0BOX:
+			ret = setup_s0box(card);
+			break;
+#endif
+#if CARD_TELESPCI
+		case ISDN_CTYPE_TELESPCI:
+			ret = setup_telespci(card);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = setup_avm_a1(card);
+			break;
+#endif
+#if CARD_AVM_A1_PCMCIA
+		case ISDN_CTYPE_A1_PCMCIA:
+			ret = setup_avm_a1_pcmcia(card);
+			break;
+#endif
+#if CARD_FRITZPCI
+		case ISDN_CTYPE_FRITZPCI:
+			ret = setup_avm_pcipnp(card);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_PNP:
+		case ISDN_CTYPE_ELSA_PCMCIA:
+		case ISDN_CTYPE_ELSA_PCI:
+			ret = setup_elsa(card);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = setup_ix1micro(card);
+			break;
+#endif
+#if CARD_DIEHLDIVA
+		case ISDN_CTYPE_DIEHLDIVA:
+			ret = setup_diva(card);
+			break;
+#endif
+#if CARD_ASUSCOM
+		case ISDN_CTYPE_ASUSCOM:
+			ret = setup_asuscom(card);
+			break;
+#endif
+#if CARD_TELEINT
+		case ISDN_CTYPE_TELEINT:
+			ret = setup_TeleInt(card);
+			break;
+#endif
+#if CARD_SEDLBAUER
+		case ISDN_CTYPE_SEDLBAUER:
+		case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+		case ISDN_CTYPE_SEDLBAUER_FAX:
+			ret = setup_sedlbauer(card);
+			break;
+#endif
+#if CARD_SPORTSTER
+		case ISDN_CTYPE_SPORTSTER:
+			ret = setup_sportster(card);
+			break;
+#endif
+#if CARD_MIC
+		case ISDN_CTYPE_MIC:
+			ret = setup_mic(card);
+			break;
+#endif
+#if CARD_NETJET
+		case ISDN_CTYPE_NETJET:
+			ret = setup_netjet(card);
+			break;
+#endif
+#if CARD_TELES3C
+		case ISDN_CTYPE_TELES3C:
+			ret = setup_t163c(card);
+			break;
+#endif
+#if CARD_NICCY
+		case ISDN_CTYPE_NICCY:
+			ret = setup_niccy(card);
+			break;
+#endif
+#if CARD_AMD7930
+		case ISDN_CTYPE_AMD7930:
+			ret = setup_amd7930(card);
+			break;
+#endif
+		default:
+			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
+			       card->typ);
+			ll_unload(cs);
+			restore_flags(flags);
+			return (0);
+	}
+	if (!ret) {
+		ll_unload(cs);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for isac rcvbuf\n");
+		return (1);
+	}
+	cs->rcvidx = 0;
+	cs->tx_skb = NULL;
+	cs->tx_cnt = 0;
+	cs->event = 0;
+	cs->tqueue.next = 0;
+	cs->tqueue.sync = 0;
+	cs->tqueue.data = cs;
+
+	skb_queue_head_init(&cs->rq);
+	skb_queue_head_init(&cs->sq);
+
+	init_bcstate(cs, 0);
+	init_bcstate(cs, 1);
+	ret = init_card(cs);
+	if (ret) {
+		closecard(cardnr);
+		restore_flags(flags);
+		return (0);
+	}
+	init_tei(cs, cs->protocol);
+	CallcNewChan(cs);
+	/* ISAR needs firmware download first */
+	if (!test_bit(HW_ISAR, &cs->HW_Flags))
+		ll_run(cs);
+	restore_flags(flags);
+	return (1);
+}
+
+HISAX_INITFUNC(void
+HiSax_shiftcards(int idx))
 {
 	int i;
-	char tmp[64], rev[64];
-	char *r = rev;
+
+	for (i = idx; i < (HISAX_MAX_CARDS - 1); i++)
+		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+HISAX_INITFUNC(int
+HiSax_inithardware(int *busy_flag))
+{
+	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, busy_flag)) {
+			foundcards++;
+			i++;
+		} else {
+			printk(KERN_WARNING "HiSax: Card %s not installed !\n",
+			       CardType[cards[i].typ]);
+			if (cards[i].cs)
+				kfree((void *) cards[i].cs);
+			cards[i].cs = NULL;
+			HiSax_shiftcards(i);
+		}
+	}
+	return foundcards;
+}
+
+void
+HiSax_closecard(int cardnr)
+{
+	int 	i,last=nrcards - 1;
+
+	if (cardnr>last)
+		return;
+	if (cards[cardnr].cs) {
+		ll_stop(cards[cardnr].cs);
+		release_tei(cards[cardnr].cs);
+		closecard(cardnr);
+		free_irq(cards[cardnr].cs->irq, cards[cardnr].cs);
+		kfree((void *) cards[cardnr].cs);
+		cards[cardnr].cs = NULL;
+	}
+	i = cardnr;
+	while (i!=last) {
+		cards[i] = cards[i+1];
+		i++;
+	}
+	nrcards--;
+}
+
+void
+HiSax_reportcard(int cardnr)
+{
+	struct IsdnCardState *cs = cards[cardnr].cs;
+	struct PStack *stptr;
+	struct l3_process *pc;
+	int j, i = 1;
+
+	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
+	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+		(ulong) & HiSax_reportcard);
+	printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
+	printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc0 flg %x\n",
+		cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag);
+	printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n",
+		cs->bcs[0].mode, cs->bcs[0].channel);
+	printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n",
+		cs->bcs[1].mode, cs->bcs[1].channel);
+	printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist));
+	stptr = cs->stlist;
+	while (stptr != NULL) {
+		printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr);
+		printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp);
+		printk(KERN_DEBUG "HiSax:   tei %d sapi %d\n",
+		       stptr->l2.tei, stptr->l2.sap);
+		printk(KERN_DEBUG "HiSax:      man 0x%lX\n", (ulong) stptr->ma.layer);
+		pc = stptr->l3.proc;
+		while (pc) {
+			printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref,
+			       (ulong) pc);
+			printk(KERN_DEBUG "HiSax:    state %d  st 0x%lX chan 0x%lX\n",
+			    pc->state, (ulong) pc->st, (ulong) pc->chan);
+			pc = pc->next;
+		}
+		stptr = stptr->next;
+		i++;
+	}
+	for (j = 0; j < 2; j++) {
+		printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j,
+		       (ulong) & cs->channel[j]);
+		stptr = cs->channel[j].b_st;
+		i = 1;
+		while (stptr != NULL) {
+			printk(KERN_DEBUG "HiSax:  b_st%d 0x%lX\n", i, (ulong) stptr);
+			printk(KERN_DEBUG "HiSax:    man 0x%lX\n", (ulong) stptr->ma.layer);
+			stptr = stptr->next;
+			i++;
+		}
+	}
+}
+
+
+__initfunc(int
+HiSax_init(void))
+{
+	int i;
+
 #ifdef MODULE
 	int nzproto = 0;
+#ifdef CONFIG_HISAX_ELSA
+	if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) {
+		/* we have exported  and return in this case */
+		register_elsa_symbols();
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+	if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) {
+		/* we have to export  and return in this case */
+		register_symtab(&hisax_syms_sedl);
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+	if (type[0] == ISDN_CTYPE_A1_PCMCIA) {
+		/* we have to export  and return in this case */
+		register_avm_a1_symbols();
+		return 0;
+	}
+#endif
 #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);
-
+	HiSaxVersion();
 #ifdef MODULE
 	if (id)			/* If id= string used */
 		HiSax_id = id;
-	for (i = 0; i < 16; i++) {
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
 		cards[i].typ = type[i];
 		if (protocol[i]) {
 			cards[i].protocol = protocol[i];
@@ -343,37 +1306,49 @@
 				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 */
+#ifdef IO0_IO1
 			case ISDN_CTYPE_PNP:
+			case ISDN_CTYPE_NICCY:
 				cards[i].para[0] = irq[i];
 				cards[i].para[1] = io0[i];
 				cards[i].para[2] = io1[i];
 				break;
-#endif
-			case ISDN_CTYPE_A1:
+			case ISDN_CTYPE_COMPAQ_ISA:
 				cards[i].para[0] = irq[i];
-				cards[i].para[1] = io[i];
+				cards[i].para[1] = io0[i];
+				cards[i].para[2] = io1[i];
+				cards[i].para[3] = io[i];
 				break;
-
+#endif
 			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_16_3:
+			case ISDN_CTYPE_TELESPCMCIA:
+			case ISDN_CTYPE_A1:
+			case ISDN_CTYPE_A1_PCMCIA:
+			case ISDN_CTYPE_ELSA_PNP:
+			case ISDN_CTYPE_ELSA_PCMCIA:
 			case ISDN_CTYPE_IX1MICROR2:
+			case ISDN_CTYPE_DIEHLDIVA:
+			case ISDN_CTYPE_ASUSCOM:
+			case ISDN_CTYPE_TELEINT:
+			case ISDN_CTYPE_SEDLBAUER:
+			case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+			case ISDN_CTYPE_SEDLBAUER_FAX:
+			case ISDN_CTYPE_SPORTSTER:
+			case ISDN_CTYPE_MIC:
+			case ISDN_CTYPE_TELES3C:
+			case ISDN_CTYPE_S0BOX:
+			case ISDN_CTYPE_FRITZPCI:
 				cards[i].para[0] = irq[i];
 				cards[i].para[1] = io[i];
 				break;
-
+			case ISDN_CTYPE_ELSA_PCI:
+			case ISDN_CTYPE_NETJET:
+			case ISDN_CTYPE_AMD7930:
+			case ISDN_CTYPE_TELESPCI:
+				break;
 		}
 	}
 	if (!nzproto) {
@@ -386,29 +1361,31 @@
 		HiSax_id = HiSaxID;
 	if (!HiSaxID[0])
 		strcpy(HiSaxID, "HiSax");
-	for (i = 0; i < 16; i++)
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
 		if (cards[i].typ > 0)
 			nrcards++;
 	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
 	       nrcards, (nrcards > 1) ? "s" : "");
 
 	CallcNew();
+	Isdnl3New();
 	Isdnl2New();
-	if (HiSax_inithardware()) {
+	TeiNew();
+	Isdnl1New();
+	if (HiSax_inithardware(NULL)) {
 		/* 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");
+		printk(KERN_INFO "HiSax: module installed\n");
 #endif
 		return (0);
 	} else {
+		Isdnl1Free();
+		TeiFree();
 		Isdnl2Free();
+		Isdnl3Free();
 		CallcFree();
 		return -EIO;
 	}
@@ -418,8 +1395,172 @@
 void
 cleanup_module(void)
 {
-	HiSax_closehardware();
-	printk(KERN_NOTICE "HiSax module removed\n");
+	int cardnr = nrcards -1;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	while(cardnr>=0)
+		HiSax_closecard(cardnr--);
+	Isdnl1Free();
+	TeiFree();
+	Isdnl2Free();
+	Isdnl3Free();
+	CallcFree();
+	restore_flags(flags);
+	printk(KERN_INFO "HiSax module removed\n");
 }
+#endif
+
+#ifdef CONFIG_HISAX_ELSA
+int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+#ifdef MODULE
+	int i;
+	int nzproto = 0;
 
+	nrcards = 0;
+	HiSaxVersion();
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	/* Initialize all 8 structs, even though we only accept
+	   two pcmcia cards
+	   */
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
+		cards[i].para[0] = irq[i];
+		cards[i].para[1] = io[i];
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+	}
+	cards[0].para[0] = pcm_irq;
+	cards[0].para[1] = (int)pcm_iob;
+	cards[0].protocol = prot;
+	cards[0].typ = 10;
+	nzproto = 1;
+
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	Isdnl1New();
+	CallcNew();
+	Isdnl3New();
+	Isdnl2New();
+	TeiNew();
+	HiSax_inithardware(busy_flag);
+	printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+	return (0);
+}
+#endif
+
+#ifdef CONFIG_HISAX_SEDLBAUER
+int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+#ifdef MODULE
+	int i;
+	int nzproto = 0;
+
+	nrcards = 0;
+	HiSaxVersion();
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	/* Initialize all 8 structs, even though we only accept
+	   two pcmcia cards
+	   */
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
+		cards[i].para[0] = irq[i];
+		cards[i].para[1] = io[i];
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+	}
+	cards[0].para[0] = pcm_irq;
+	cards[0].para[1] = (int)pcm_iob;
+	cards[0].protocol = prot;
+	cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+	nzproto = 1;
+
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	CallcNew();
+	Isdnl3New();
+	Isdnl2New();
+	Isdnl1New();
+	TeiNew();
+	HiSax_inithardware(busy_flag);
+	printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+	return (0);
+}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+int avm_a1_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+#ifdef MODULE
+	int i;
+	int nzproto = 0;
+
+	nrcards = 0;
+	HiSaxVersion();
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	/* Initialize all 16 structs, even though we only accept
+	   two pcmcia cards
+	   */
+	for (i = 0; i < 16; i++) {
+		cards[i].para[0] = irq[i];
+		cards[i].para[1] = io[i];
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+	}
+	cards[0].para[0] = pcm_irq;
+	cards[0].para[1] = (int)pcm_iob;
+	cards[0].protocol = prot;
+	cards[0].typ = ISDN_CTYPE_A1_PCMCIA;
+	nzproto = 1;
+
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	Isdnl1New();
+	CallcNew();
+	Isdnl3New();
+	Isdnl2New();
+	TeiNew();
+	HiSax_inithardware(busy_flag);
+	printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+	return (0);
+}
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/diva.c linux/drivers/isdn/hisax/diva.c
--- v2.0.35/linux/drivers/isdn/hisax/diva.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/diva.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,613 @@
+/* $Id: diva.c,v 1.1.2.11 1998/11/05 21:13:51 keil Exp $
+
+ * diva.c     low level stuff for Eicon.Diehl Diva Family ISDN cards
+ *
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations
+ *
+ *
+ * $Log: diva.c,v $
+ * Revision 1.1.2.11  1998/11/05 21:13:51  keil
+ * minor fixes
+ *
+ * Revision 1.1.2.10  1998/11/03 00:06:10  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.9  1998/06/27 21:58:34  keil
+ * fix release of diva 2.01
+ *
+ * Revision 1.1.2.8  1998/05/27 18:05:11  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.7  1998/04/24 13:37:13  keil
+ * Support for DIVA 2.01 IPAC
+ *
+ * Revision 1.1.2.6  1998/04/08 22:05:21  keil
+ * Forgot PCI fix
+ *
+ * Revision 1.1.2.5  1998/04/08 21:49:27  keil
+ * New init; fix PCI for more as one card
+ *
+ * Revision 1.1.2.4  1998/03/07 23:15:14  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
+ * Revision 1.1.2.3  1998/01/27 22:37:35  keil
+ * fast io
+ *
+ * Revision 1.1.2.2  1997/11/15 18:50:44  keil
+ * new common init function
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:36  keil
+ * new files on 2.0
+ *
+ * Revision 1.1  1997/09/18 17:11:20  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "ipac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+extern const char *CardType[];
+
+const char *Diva_revision = "$Revision: 1.1.2.11 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define DIVA_HSCX_DATA		0
+#define DIVA_HSCX_ADR		4
+#define DIVA_ISA_ISAC_DATA	2
+#define DIVA_ISA_ISAC_ADR	6
+#define DIVA_ISA_CTRL		7
+#define DIVA_IPAC_ADR		0
+#define DIVA_IPAC_DATA		1
+
+#define DIVA_PCI_ISAC_DATA	8
+#define DIVA_PCI_ISAC_ADR	0xc
+#define DIVA_PCI_CTRL		0x10
+
+/* SUB Types */
+#define DIVA_ISA	1
+#define DIVA_PCI	2
+#define DIVA_IPAC_ISA	3
+
+/* PCI stuff */
+#define PCI_VENDOR_EICON_DIEHL	0x1133
+#define PCI_DIVA20PRO_ID	0xe001
+#define PCI_DIVA20_ID		0xe002
+#define PCI_DIVA20PRO_U_ID	0xe003
+#define PCI_DIVA20_U_ID		0xe004
+
+/* CTRL (Read) */
+#define DIVA_IRQ_STAT	0x01
+#define DIVA_EEPROM_SDA	0x02
+/* CTRL (Write) */
+#define DIVA_IRQ_REQ	0x01
+#define DIVA_RESET	0x08
+#define DIVA_EEPROM_CLK	0x40
+#define DIVA_PCI_LED_A	0x10
+#define DIVA_PCI_LED_B	0x20
+#define DIVA_ISA_LED_A	0x20
+#define DIVA_ISA_LED_B	0x40
+#define DIVA_IRQ_CLR	0x80
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(readreg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \
+                cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+diva_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval, stat = 0;
+	int cnt=8;
+
+	if (!cs) {
+		printk(KERN_WARNING "Diva: Spurious interrupt!\n");
+		return;
+	}
+	while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) {
+		val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40);
+		if (val) {
+			hscx_int_main(cs, val);
+			stat |= 1;
+		}
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA);
+		if (val) {
+			isac_interrupt(cs, val);
+			stat |= 2;
+		}
+		cnt--;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "Diva: IRQ LOOP\n");
+	if (stat & 1) {
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+diva_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	int icnt=20;
+
+	if (!cs) {
+		printk(KERN_WARNING "Diva: Spurious interrupt!\n");
+		return;
+	}
+	ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n");
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0);
+}
+
+
+void
+release_io_diva(struct IsdnCardState *cs)
+{
+	int bytecnt;
+
+	if (cs->subtyp != DIVA_IPAC_ISA) {
+		del_timer(&cs->hw.diva.tl);
+		if (cs->hw.diva.cfg_reg)
+			byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
+	}
+	if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA))
+		bytecnt = 8;
+	else
+		bytecnt = 32;
+	if (cs->hw.diva.cfg_reg) {
+		release_region(cs->hw.diva.cfg_reg, bytecnt);
+	}
+}
+
+static void
+reset_diva(struct IsdnCardState *cs)
+{
+	long flags;
+
+	save_flags(flags);
+	sti();
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
+		schedule();
+	} else {
+		cs->hw.diva.ctrl_reg = 0;        /* Reset On */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		if (cs->subtyp == DIVA_ISA)
+			cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
+		else
+			cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	}
+	restore_flags(flags);
+}
+
+#define DIVA_ASSIGN 1
+
+static void
+diva_led_handler(struct IsdnCardState *cs)
+{
+	int blink = 0;
+
+	if (cs->subtyp == DIVA_IPAC_ISA)
+		return;
+	del_timer(&cs->hw.diva.tl);
+	if (cs->hw.diva.status & DIVA_ASSIGN)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+	else {
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+		blink = 250;
+	}
+	if (cs->hw.diva.status & 0xf000)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+	else if (cs->hw.diva.status & 0x0f00) {
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+		blink = 500;
+	} else
+		cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B);
+
+	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.diva.tl);
+		cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.diva.tl);
+	}
+}
+
+static int
+Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_int irq_flag = I4L_IRQ_FLAG;
+
+	switch (mt) {
+		case CARD_RESET:
+			reset_diva(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_diva(cs);
+			return(0);
+		case CARD_SETIRQ:
+			if (cs->subtyp == DIVA_PCI)
+				irq_flag |= SA_SHIRQ;
+			if (cs->subtyp == DIVA_IPAC_ISA) {
+				return(request_irq(cs->irq, &diva_interrupt_ipac,
+						irq_flag, "HiSax", cs));
+			} else {
+				return(request_irq(cs->irq, &diva_interrupt,
+						irq_flag, "HiSax", cs));
+			}
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
+		case (MDL_REMOVE | REQUEST):
+			cs->hw.diva.status = 0;
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			cs->hw.diva.status |= DIVA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((long)arg)
+				cs->hw.diva.status |=  0x0200;
+			else
+				cs->hw.diva.status |=  0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((long)arg)
+				cs->hw.diva.status |=  0x2000;
+			else
+				cs->hw.diva.status |=  0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((long)arg) {
+				cs->hw.diva.status &=  ~0x2000;
+				cs->hw.diva.status &=  ~0x0200;
+			} else {
+				cs->hw.diva.status &=  ~0x1000;
+				cs->hw.diva.status &=  ~0x0100;
+			}
+			break;
+	}
+	if (cs->subtyp != DIVA_IPAC_ISA)
+		diva_led_handler(cs);
+	return(0);
+}
+
+
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_diva(struct IsdnCard *card))
+{
+	int bytecnt;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Diva_revision);
+	printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_DIEHLDIVA)
+		return(0);
+	cs->hw.diva.status = 0;
+	if (card->para[1]) {
+		cs->hw.diva.ctrl_reg = 0;
+		cs->hw.diva.cfg_reg = card->para[1];
+		val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR,
+			cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+		if (val == 1) {
+			cs->subtyp = DIVA_IPAC_ISA;
+			cs->hw.diva.ctrl = 0;
+			cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		} else {
+			cs->subtyp = DIVA_ISA;
+			cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
+			cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+		}
+		cs->irq = card->para[0];
+		bytecnt = 8;
+	} else {
+#if CONFIG_PCI
+		u_char pci_bus, pci_device_fn, pci_irq;
+		u_int pci_ioaddr;
+
+		cs->subtyp = 0;
+		for (; pci_index < 0xff; pci_index++) {
+			if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
+			   PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+				cs->subtyp = DIVA_PCI;
+			else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
+			   PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+			   	cs->subtyp = DIVA_PCI;
+			else
+				break;
+			/* get IRQ */
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &pci_irq);
+
+			/* get IO address */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_2, &pci_ioaddr);
+			if (cs->subtyp)
+				break;
+		}
+		if (!cs->subtyp) {
+			printk(KERN_WARNING "Diva: No PCI card found\n");
+			return(0);
+		}
+		pci_index++;
+		if (!pci_irq) {
+			printk(KERN_WARNING "Diva: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!pci_ioaddr) {
+			printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		pci_ioaddr &= ~3; /* remove io/mem flag */
+		cs->hw.diva.cfg_reg = pci_ioaddr;
+		cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL;
+		cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA;
+		cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA;
+		cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR;
+		cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR;
+		cs->irq = pci_irq;
+		bytecnt = 32;
+#else
+		printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Diva: unable to config DIVA PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+
+	printk(KERN_INFO
+		"Diva: %s card configured at 0x%x IRQ %d\n",
+		(cs->subtyp == DIVA_PCI) ? "PCI" :
+		(cs->subtyp == DIVA_ISA) ? "ISA" : "IPAC",
+		cs->hw.diva.cfg_reg, cs->irq);
+	if (check_region(cs->hw.diva.cfg_reg, bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.diva.cfg_reg,
+		       cs->hw.diva.cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn");
+	}
+
+	reset_diva(cs);
+	cs->BC_Read_Reg  = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Diva_card_msg;
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		cs->readisac  = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo  = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+	} else {
+		cs->hw.diva.tl.function = (void *) diva_led_handler;
+		cs->hw.diva.tl.data = (long) cs;
+		init_timer(&cs->hw.diva.tl);
+		cs->readisac  = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo  = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		ISACVersion(cs, "Diva:");
+		if (HscxVersion(cs, "Diva:")) {
+			printk(KERN_WARNING
+		       "Diva: wrong HSCX versions check IO address\n");
+			release_io_diva(cs);
+			return (0);
+		}
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c
--- v2.0.35/linux/drivers/isdn/hisax/elsa.c	Tue Aug  5 09:00:12 1997
+++ linux/drivers/isdn/hisax/elsa.c	Sun Nov 15 10:32:58 1998
@@ -1,1248 +1,876 @@
-/* $Id: elsa.c,v 1.14 1997/04/13 19:53:25 keil Exp $
+/* $Id: elsa.c,v 1.14.2.16 1998/11/05 21:13:55 keil Exp $
 
  * elsa.c     low level stuff for Elsa isdn cards
  *
- * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
  *
  * Thanks to    Elsa GmbH for documents and informations
  *
+ *              Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
+ *              for ELSA PCMCIA support
+ *
  *
  * $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.14.2.16  1998/11/05 21:13:55  keil
+ * minor fixes
  *
- * Revision 1.13  1997/04/07 22:58:07  keil
- * need include config.h
+ * Revision 1.14.2.15  1998/11/03 00:06:14  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.12  1997/04/06 22:54:14  keil
- * Using SKB's
+ * Revision 1.14.2.14  1998/10/25 19:43:58  fritz
+ * Removed a compiler warning
+ *
+ * Revision 1.14.2.13  1998/10/25 17:47:48  fritz
+ * Line power status only valid for ISA cards.
+ *
+ * Revision 1.14.2.12  1998/09/27 13:05:53  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
- * Revision 1.11  1997/03/23 21:45:46  keil
- * Add support for ELSA PCMCIA
+ * Revision 1.14.2.11  1998/05/27 18:05:14  keil
+ * HiSax 3.0
  *
- * Revision 1.10  1997/03/12 21:42:19  keil
- * Bugfix: IRQ hangs with QS1000
+ * Revision 1.14.2.10  1998/04/11 18:46:03  keil
+ * QS3000PCI support, changes for arcofi
  *
- * Revision 1.9  1997/03/04 15:57:39  keil
- * bugfix IRQ reset Quickstep, ELSA PC changes, some stuff for new cards
+ * Revision 1.14.2.9  1998/04/08 21:44:36  keil
+ * new init; fix PCI for more as one card
  *
- * Revision 1.8  1997/01/27 15:51:48  keil
- * SMP proof,cosmetics
+ * Revision 1.14.2.8  1998/03/07 23:15:15  tsbogend
+ * made HiSax working on Linux/Alpha
  *
- * Revision 1.7  1997/01/21 22:20:48  keil
- * Elsa Quickstep support
+ * Revision 1.14.2.7  1998/01/27 22:37:36  keil
+ * fast io
  *
- * Revision 1.6  1997/01/09 18:22:46  keil
- * one more PCC-8 fix
+ * Revision 1.14.2.6  1998/01/11 22:57:10  keil
+ * add PCMCIA maintainer Klaus
  *
- * Revision 1.5  1996/12/08 19:46:14  keil
- * PCC-8 correct IRQs; starting ARCOFI support
+ * Revision 1.14.2.5  1997/11/15 18:50:47  keil
+ * new common init function
  *
- * Revision 1.4  1996/11/18 20:50:54  keil
- * with PCF Pro release 16 Byte IO
+ * Revision 1.14.2.4  1997/10/17 22:13:44  keil
+ * update to last hisax version
  *
- * Revision 1.3  1996/11/18 15:33:04  keil
- * PCC and PCFPro support
+ * Revision 2.1  1997/07/27 21:47:08  keil
+ * new interface structures
+ *
+ * Revision 2.0  1997/06/26 11:02:40  keil
+ * New Layer and card interface
+ *
+ * Revision 1.14  1997/04/13 19:53:25  keil
+ * Fixed QS1000 init, change in IRQ check delay for SMP
  *
- * Revision 1.2  1996/10/27 22:08:03  keil
- * cosmetic changes
+ * Revision 1.13  1997/04/07 22:58:07  keil
+ * need include config.h
  *
- * Revision 1.1  1996/10/13 20:04:52  keil
- * Initial revision
+ * Revision 1.12  1997/04/06 22:54:14  keil
+ * Using SKB's
  *
+ * old changes removed KKe
  *
  */
 
-#define ARCOFI_USE	0
-
 #define __NO_VERSION__
 #include <linux/config.h>
-#include "siemens.h"
 #include "hisax.h"
-#include "elsa.h"
+#include "arcofi.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
 
 extern const char *CardType[];
 
-const char *Elsa_revision = "$Revision: 1.14 $";
+const char *Elsa_revision = "$Revision: 1.14.2.16 $";
 const char *Elsa_Types[] =
 {"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
- "PCMCIA", "QS 1000", "QS 3000"};
+ "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"};
 
 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);
-}
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
+#define ELSA_ISAC	0
+#define ELSA_ISAC_PCM	1
+#define ELSA_ITAC	1
+#define ELSA_HSCX	2
+#define ELSA_ALE	3
+#define ELSA_ALE_PCM	4
+#define ELSA_CONTROL	4
+#define ELSA_CONFIG	5
+#define ELSA_START_TIMER 6
+#define ELSA_TRIG_IRQ	7
+
+#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
+#define ELSA_QS1000PCI 9
+#define ELSA_QS3000PCI 10
+
+/* PCI stuff */
+#define PCI_VENDOR_ELSA	0x1048
+#define PCI_QS1000_ID	0x1000
+#define PCI_QS3000_ID	0x3000
+#define ELSA_PCI_IRQ_MASK	0x04
+
+/* 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 ELSA_TIMER_RUN       0x02	/* Bit 1 des Config-Reg     */
+#define ELSA_TIMER_RUN_PCC8  0x01	/* Bit 0 des Config-Reg  bei PCC */
+#define ELSA_IRQ_IDX       0x38	/* Bit 3,4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PCC8  0x30	/* Bit 4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PC    0x0c	/* Bit 2,3 des Config-Reg */
+
+/* Control-Register (Write) */
+#define ELSA_LINE_LED        0x02	/* Bit 1 Gelbe LED */
+#define ELSA_STAT_LED        0x08	/* Bit 3 Gruene LED */
+#define ELSA_ISDN_RESET      0x20	/* Bit 5 Reset-Leitung */
+#define ELSA_ENA_TIMER_INT   0x80	/* Bit 7 Freigabe Timer Interrupt */
+
+/* ALE-Register (Read) */
+#define ELSA_HW_RELEASE      0x07	/* Bit 0-2 Hardwarerkennung */
+#define ELSA_S0_POWER_BAD    0x08	/* Bit 3 S0-Bus Spannung fehlt */
+
+/* Status Flags */
+#define ELSA_TIMER_AKTIV 1
+#define ELSA_BAD_PWR     2
+#define ELSA_ASSIGN      4
+
+#define RS_ISR_PASS_LIMIT 256
+#define _INLINE_ inline
+#define FLG_MODEM_ACTIVE 1
+/* IPAC AUX */
+#define ELSA_IPAC_LINE_LED	0x40	/* Bit 6 Gelbe LED */
+#define ELSA_IPAC_STAT_LED	0x80	/* Bit 7 Gruene LED */
+
+const u_char ARCOFI_VERSION[] = {2,0xa0,0};
+const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */
+const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */
+const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */
+const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */
+const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}; /* RX */
+const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */
+const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR UP */
+const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* Normal OP */
+const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12};
 
-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 void set_arcofi(struct IsdnCardState *cs, int bc);
 
-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);
-}
+#if ARCOFI_USE
+#include "elsa_ser.c"
+#endif
 
 static inline u_char
-readisac(unsigned int adr, u_char off)
+readreg(unsigned int ale, 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);
+	byteout(ale, off);
+	ret = bytein(adr);
 	restore_flags(flags);
 	return (ret);
 }
 
 static inline void
-read_fifo_isac(unsigned int adr, u_char * data, int size)
+readfifo(unsigned int ale, unsigned int adr, u_char off, 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);
+	byteout(ale, off);
+	insb(adr, data, size);
 }
 
 
 static inline void
-writeisac(unsigned int adr, u_char off, u_char data)
+writereg(unsigned int ale, 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);
+	byteout(ale, off);
+	byteout(adr, data);
 	restore_flags(flags);
 }
 
 static inline void
-write_fifo_isac(unsigned int adr, u_char * data, int size)
+writefifo(unsigned int ale, unsigned int adr, u_char off, 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);
+	byteout(ale, off);
+	outsb(adr, data, size);
 }
 
-#ifdef CONFIG_HISAX_ELSA_PCC
-static inline u_char
-readitac(unsigned int adr, u_char off)
-{
-	register u_char ret;
-	long flags;
+/* Interface functions */
 
-	save_flags(flags);
-	cli();
-	byteout(adr + CARD_ALE, off);
-	ret = bytein(adr + CARD_ITAC);
-	restore_flags(flags);
-	return (ret);
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset));
 }
 
-static inline void
-writeitac(unsigned int adr, u_char off, u_char data)
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	byteout(adr + CARD_ALE, off);
-	byteout(adr + CARD_ITAC, data);
-	restore_flags(flags);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value);
 }
 
-static inline int
-TimerRun(struct IsdnCardState *sp)
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
 }
 
-static inline void
-elsa_led_handler(struct IsdnCardState *sp)
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-
-	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);
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
 }
-#endif
 
-static inline void
-waitforCEC(int adr, int hscx)
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
 {
-	int to = 50;
-
-	while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "Elsa: waitforCEC timeout\n");
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80));
 }
 
-
-static inline void
-waitforXFW(int adr, int hscx)
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	int to = 50;
-
-	while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "Elsa: waitforXFW timeout\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value);
 }
 
-static inline void
-writehscxCMDR(int adr, int hscx, u_char data)
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC(adr, hscx);
-	writehscx(adr, hscx, HSCX_CMDR, data);
-	restore_flags(flags);
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
 }
 
-/*
- * fast interrupt here
- */
-
-
 static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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));
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
 }
 
-void
-elsa_report(struct IsdnCardState *sp)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	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);
+	return (readreg(cs->hw.elsa.ale,
+			cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0)));
 }
 
-/*
- * HSCX stuff goes here
- */
-
 static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	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);
-	}
+	writereg(cs->hw.elsa.ale,
+		 cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value);
 }
 
-static void
-hscx_fill_fifo(struct HscxState *hsp)
+static inline u_char
+readitac(struct IsdnCardState *cs, u_char off)
 {
-	struct IsdnCardState *sp = hsp->sp;
-	int more, count;
-	u_char *ptr;
+	register u_char ret;
 	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);
+	byteout(cs->hw.elsa.ale, off);
+	ret = bytein(cs->hw.elsa.itac);
 	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);
-	}
+	return (ret);
 }
 
 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)
+writeitac(struct IsdnCardState *cs, u_char off, u_char data)
 {
-	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);
+	byteout(cs->hw.elsa.ale, off);
+	byteout(cs->hw.elsa.itac, data);
 	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)
+static inline int
+TimerRun(struct IsdnCardState *cs)
 {
-	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;
+	register u_char v;
 
-	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);
-	}
+	v = bytein(cs->hw.elsa.cfg);
+	if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000))
+		return (0 == (v & ELSA_TIMER_RUN));
+	else if (cs->subtyp == ELSA_PCC8)
+		return (v & ELSA_TIMER_RUN_PCC8);
+	return (v & ELSA_TIMER_RUN);
 }
+/*
+ * fast interrupt HSCX stuff goes here
+ */
 
-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);
-}
+#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data)
 
-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
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-	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
-		}
-	}
-}
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-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);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val;
+	int icnt=20;
 
-	sp = (struct IsdnCardState *) irq2dev_map[intno];
-
-	if (!sp) {
+	if (!cs) {
 		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);
+	if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
+	/* The card tends to generate interrupts while being removed
+	   causing us to just crash the kernel. bad. */
+		printk(KERN_WARNING "Elsa: card not available!\n");
+		return;
+	}
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
 		}
 	}
 #endif
-	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 	}
-	val = readisac(sp->cfg_reg, ISAC_ISTA);
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, 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");
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		icnt--;
 		goto Start_HSCX;
 	}
-	val = readisac(sp->cfg_reg, ISAC_ISTA);
-	if (val) {
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "ISAC IntStat after IntRoutine");
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		icnt--;
 		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);
+	if (!icnt)
+		printk(KERN_WARNING"ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF);
+	if (cs->hw.elsa.status & ELSA_TIMER_AKTIV) {
+		if (!TimerRun(cs)) {
+			/* Timer Restart */
+			byteout(cs->hw.elsa.timer, 0);
+			cs->hw.elsa.counter++;
+		}
 	}
-#endif
-	writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0);
-	writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0);
-	writeisac(sp->cfg_reg, ISAC_MASK, 0x0);
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+	}
+	if (cs->hw.elsa.trig)
+		byteout(cs->hw.elsa.trig, 0x00);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0);
 }
 
-
 static void
-initisac(struct IsdnCardState *sp)
+elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
 {
-	unsigned int adr = sp->cfg_reg;
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	int icnt=20;
 
-	/* 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;
+	if (!cs) {
+		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
+		return;
 	}
-	writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00);
+	val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
+	if (!(val & ELSA_PCI_IRQ_MASK))
+		return;
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
+		}
+	}
+#endif
+	ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0);
 }
 
 void
-release_io_elsa(struct IsdnCard *card)
+release_io_elsa(struct IsdnCardState *cs)
 {
 	int bytecnt = 8;
 
-	if (card->sp->subtyp == ELSA_PCFPRO)
+	del_timer(&cs->hw.elsa.tl);
+	if (cs->hw.elsa.ctrl)
+		byteout(cs->hw.elsa.ctrl, 0);	/* LEDs Out */
+	if (cs->subtyp == ELSA_QS1000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x01);  /* disable IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		bytecnt = 2;
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+	if (cs->subtyp == ELSA_QS3000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* disable ELSA PCI IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+	if ((cs->subtyp == ELSA_PCFPRO) ||
+		(cs->subtyp == ELSA_QS3000) ||
+		(cs->subtyp == ELSA_PCF) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
 		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
+		release_modem(cs);
+	}
+	if (cs->hw.elsa.base)
+		release_region(cs->hw.elsa.base, bytecnt);
 }
 
 static void
-clear_pending_ints(struct IsdnCardState *sp)
+reset_elsa(struct IsdnCardState *cs)
 {
-#ifdef CONFIG_HISAX_ELSA_PCMCIA
-	int val;
-	char tmp[64];
+	long flags;
 
-	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);
+	if (cs->hw.elsa.timer) {
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= 0x50;
+		cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET;	/* Reset On */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET;	/* Reset Off */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		if (cs->hw.elsa.trig)
+			byteout(cs->hw.elsa.trig, 0xff);
 	}
-#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);
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+		save_flags(flags);
+		sti();
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+		schedule();
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
+		schedule();
+		restore_flags(flags);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		if (cs->subtyp == ELSA_QS1000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+		else if (cs->subtyp == ELSA_QS3000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */
 	}
-#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;
+init_arcofi(struct IsdnCardState *cs) {
+	send_arcofi(cs, ARCOFI_XOP_0, 1, 0);
+/*	send_arcofi(cs, ARCOFI_XOP_F, 1);
+*/
+}
+
+#define ARCDEL 500
+
+static void
+set_arcofi(struct IsdnCardState *cs, int bc) {
 	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);
+	debugl1(cs,"set_arcofi bc=%d", bc);
 	save_flags(flags);
 	sti();
-	HZDELAY(3);
+	send_arcofi(cs, ARCOFI_XOP_0, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_5, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_6, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_7, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_8, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_COP_9, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_SOP_F, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_XOP_1, bc, 0);
+	udelay(ARCDEL);
+	send_arcofi(cs, ARCOFI_XOP_F, bc, 0);
 	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);
+	debugl1(cs,"end set_arcofi bc=%d", bc);
+}
+
+static int
+check_arcofi(struct IsdnCardState *cs)
+{
+#if ARCOFI_USE
+	int arcofi_present = 0;
+	char tmp[40];
+	char *t;
+	u_char *p;
+
+	if (!cs->mon_tx)
+		if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "ISAC MON TX out of buffers!");
+			return(0);
+		}
+	send_arcofi(cs, ARCOFI_VERSION, 0, 1);
+	if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
+		if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) {
+			debugl1(cs, "Arcofi response received %d bytes", cs->mon_rxp);
+			p = cs->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;
+			QuickHex(t, p, cs->mon_rxp);
+			debugl1(cs, tmp);
+			if ((cs->mon_rxp == 2) && (cs->mon_rx[0] == 0xa0)) {
+				switch(cs->mon_rx[1]) {
+					case 0x80:
+						debugl1(cs, "Arcofi 2160 detected");
+						arcofi_present = 1;
+						break;
+					case 0x82:
+						debugl1(cs, "Arcofi 2165 detected");
+						arcofi_present = 2;
+						break;
+					case 0x84:
+						debugl1(cs, "Arcofi 2163 detected");
+						arcofi_present = 3;
+						break;
+					default:
+						debugl1(cs, "unknown Arcofi response");
+						break;
+				}
+			} else
+				debugl1(cs, "undefined Monitor response");
+			cs->mon_rxp = 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);
+	} else if (cs->mon_tx) {
+		debugl1(cs, "Arcofi not detected");
+	}
+	if (arcofi_present) {
+		if (cs->subtyp==ELSA_QS1000) {
+			cs->subtyp = ELSA_QS3000;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%x\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (check_region(cs->hw.elsa.base, 16)) {
+				printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base + 8,
+				cs->hw.elsa.base + 16);
+			} else
+				request_region(cs->hw.elsa.base, 16,
+					"elsa isdn modem");
+		} else if (cs->subtyp==ELSA_PCC16) {
+			cs->subtyp = ELSA_PCF;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%x\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (check_region(cs->hw.elsa.base, 16)) {
+				printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base + 8,
+				cs->hw.elsa.base + 16);
+			} else
+				request_region(cs->hw.elsa.base, 16,
+					"elsa isdn modem");
+		} else
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%x\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+		init_arcofi(cs);
+		return(1);
 	}
-	sp->mon_flg = 0;
 #endif
+	return(0);
 }
 
-int
-initelsa(struct IsdnCardState *sp)
+static void
+elsa_led_handler(struct IsdnCardState *cs)
+{
+	int blink = 0;
+
+	if (cs->subtyp == ELSA_PCMCIA)
+		return;
+	del_timer(&cs->hw.elsa.tl);
+	if (cs->hw.elsa.status & ELSA_ASSIGN)
+		cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED;
+	else if (cs->hw.elsa.status & ELSA_BAD_PWR)
+		cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED;
+	else {
+		cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED;
+		blink = 250;
+	}
+	if (cs->hw.elsa.status & 0xf000)
+		cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED;
+	else if (cs->hw.elsa.status & 0x0f00) {
+		cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED;
+		blink = 500;
+	} else
+		cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED;
+
+	if ((cs->subtyp == ELSA_QS1000PCI) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
+		u_char led = 0xff;
+		if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED)
+			led ^= ELSA_IPAC_LINE_LED;
+		if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED)
+			led ^= ELSA_IPAC_STAT_LED;
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led);
+	} else
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.elsa.tl);
+		cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.elsa.tl);
+	}
+}
+
+static int
+Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	int ret, irq_cnt, cnt = 3;
+	int len, ret = 0;
+	u_char *msg;
 	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);
-		}
+	switch (mt) {
+		case CARD_RESET:
+			reset_elsa(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_elsa(cs);
+			return(0);
+		case CARD_SETIRQ:
+			if ((cs->subtyp == ELSA_QS1000PCI) ||
+				(cs->subtyp == ELSA_QS3000PCI))
+				ret = request_irq(cs->irq, &elsa_interrupt_ipac,
+					I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs);
+			else
+				ret = request_irq(cs->irq, &elsa_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs);
+			return(ret);
+		case CARD_INIT:
+			cs->debug |= L1_DEB_IPAC;
+			inithscxisac(cs, 1);
+			if ((cs->subtyp == ELSA_QS1000) ||
+			    (cs->subtyp == ELSA_QS3000))
+			{
+				byteout(cs->hw.elsa.timer, 0);
+			}
+			if (cs->hw.elsa.trig)
+				byteout(cs->hw.elsa.trig, 0xff);
+			inithscxisac(cs, 2);
+			return(0);
+		case CARD_TEST:
+			if ((cs->subtyp == ELSA_PCMCIA) ||
+				(cs->subtyp == ELSA_QS1000PCI)) {
+				return(0);
+			} else if (cs->subtyp == ELSA_QS3000PCI) {
+				ret = 0;
+			} else {
+				save_flags(flags);
+				cs->hw.elsa.counter = 0;
+				sti();
+				cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT;
+				cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				byteout(cs->hw.elsa.timer, 0);
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + (110 * HZ) / 1000;		/* Timeout 110ms */
+				schedule();
+				restore_flags(flags);
+				cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
+				printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+				       cs->hw.elsa.counter);
+				if (abs(cs->hw.elsa.counter - 13) < 3) {
+					printk(KERN_INFO "Elsa: timer and irq OK\n");
+					ret = 0;
+				} else {
+					printk(KERN_WARNING
+					       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+					       cs->hw.elsa.counter, cs->irq);
+					ret = 1;
+				}
+			}
+#if ARCOFI_USE
+			if (check_arcofi(cs)) {
+				init_modem(cs);
+			}
 #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);
+			elsa_led_handler(cs);
+			return(ret);
+		case (MDL_REMOVE | REQUEST):
+			cs->hw.elsa.status &= 0;
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			cs->hw.elsa.status |= ELSA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((long) arg)
+				cs->hw.elsa.status |= 0x0200;
+			else
+				cs->hw.elsa.status |= 0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((long) arg)
+				cs->hw.elsa.status |= 0x2000;
+			else
+				cs->hw.elsa.status |= 0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((long) arg) {
+				cs->hw.elsa.status &= ~0x2000;
+				cs->hw.elsa.status &= ~0x0200;
 			} else {
-				reset_elsa(sp);
-				cnt--;
+				cs->hw.elsa.status &= ~0x1000;
+				cs->hw.elsa.status &= ~0x0100;
 			}
-		} else {
-			check_arcofi(sp);
-			cnt = 0;
-		}
+			break;
+		case CARD_AUX_IND:
+			if (cs->hw.elsa.MFlag) {
+				if (!arg)
+					return(0);
+				msg = arg;
+				len = *msg;
+				msg++;
+				modem_write_cmd(cs, msg, len);
+			}
+			break;
 	}
-	sp->counter = 0;
-	return (ret);
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		int pwr = bytein(cs->hw.elsa.ale);
+		if (pwr & 0x08)
+			cs->hw.elsa.status |= ELSA_BAD_PWR;
+		else
+			cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+	}
+	elsa_led_handler(cs);
+	return(ret);
 }
 
-#ifdef CONFIG_HISAX_ELSA_PCC
 static unsigned char
-probe_elsa_adr(unsigned int adr)
+probe_elsa_adr(unsigned int adr, int typ)
 {
 	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)) {
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(adr, 8)) {
 		printk(KERN_WARNING
 		       "Elsa: Probing Port 0x%x: already in use\n",
 		       adr);
@@ -1251,8 +879,8 @@
 	save_flags(flags);
 	cli();
 	for (i = 0; i < 16; i++) {
-		in1 = inb(adr + CARD_CONFIG);	/* 'toggelt' bei */
-		in2 = inb(adr + CARD_CONFIG);	/* jedem Zugriff */
+		in1 = inb(adr + ELSA_CONFIG);	/* 'toggelt' bei */
+		in2 = inb(adr + ELSA_CONFIG);	/* jedem Zugriff */
 		p16_1 += 0x04 & in1;
 		p16_2 += 0x04 & in2;
 		p8_1 += 0x02 & in1;
@@ -1283,205 +911,301 @@
 }
 
 static unsigned int
-probe_elsa(struct IsdnCardState *sp)
+probe_elsa(struct IsdnCardState *cs)
 {
 	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])))
+		if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ)))
 			break;
 	}
 	return (CARD_portlist[i]);
 }
-#endif
+
+static 	int pci_index __initdata = 0;
 
 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;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	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 "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
+	cs->hw.elsa.ctrl_reg = 0;
+	cs->hw.elsa.status = 0;
+	cs->hw.elsa.MFlag = 0;
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		cs->hw.elsa.base = card->para[0];
 		printk(KERN_INFO "Elsa: Microlink IO probing\n");
-		if (sp->cfg_reg) {
-			if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) {
+		if (cs->hw.elsa.base) {
+			if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base,
+							  cs->typ))) {
 				printk(KERN_WARNING
 				     "Elsa: no Elsa Microlink at 0x%x\n",
-				       sp->cfg_reg);
+				       cs->hw.elsa.base);
 				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) {
+			cs->hw.elsa.base = probe_elsa(cs);
+		if (cs->hw.elsa.base) {
+			cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+			cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+			cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+			cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+			cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+			cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+			cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+			val = bytein(cs->hw.elsa.cfg);
+			if (cs->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) {
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2];
+			} else if (cs->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];
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_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];
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3];
 			}
-			val = bytein(sp->cfg_reg + CARD_ALE) & 0x7;
+			val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE;
 			if (val < 3)
 				val |= 8;
 			val += 'A' - 3;
 			if (val == 'B' || val == 'C')
 				val ^= 1;
-			if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G'))
+			if ((cs->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)
+			       Elsa_Types[cs->subtyp],
+			       cs->hw.elsa.base,
+			       val, cs->irq);
+			val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD;
+			if (val) {
 				printk(KERN_WARNING
 				   "Elsa: Microlink S0 bus power bad\n");
+				cs->hw.elsa.status |= ELSA_BAD_PWR;
+			}
 		} 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;
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PNP) {
+		cs->hw.elsa.base = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = ELSA_QS1000;
+		cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+		cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+		cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+		cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+		cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+		cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
 		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;
+		       "Elsa: %s defined at 0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) {
+		cs->hw.elsa.base = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = ELSA_PCMCIA;
+		cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM;
+		cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM;
+		cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig = 0;
+		cs->hw.elsa.ctrl = 0;
+		printk(KERN_INFO
+		       "Elsa: %s defined at 0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCI) {
+#if CONFIG_PCI
+		u_char pci_bus, pci_device_fn, pci_irq;
+		u_int pci_ioaddr;
+
+		cs->subtyp = 0;
+		for (; pci_index < 0xff; pci_index++) {
+			if (pcibios_find_device(PCI_VENDOR_ELSA,
+			   PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+				cs->subtyp = ELSA_QS1000PCI;
+			else if (pcibios_find_device(PCI_VENDOR_ELSA,
+			   PCI_QS3000_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+				cs->subtyp = ELSA_QS3000PCI;
+			else
+				break;
+			/* get IRQ */
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &pci_irq);
+
+			/* get IO address */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_1, &pci_ioaddr);
+			pci_ioaddr &= ~3; /* remove io/mem flag */
+			cs->hw.elsa.cfg = pci_ioaddr;
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_3, &pci_ioaddr);
+			if (cs->subtyp)
+				break;
+		}
+		if (!cs->subtyp) {
+			printk(KERN_WARNING "Elsa: No PCI card found\n");
+			return(0);
+		}
+		pci_index++;
+		if (!pci_irq) {
+			printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!pci_ioaddr) {
+			printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		pci_ioaddr &= ~3; /* remove io/mem flag */
+		cs->hw.elsa.base = pci_ioaddr;
+		cs->hw.elsa.ale  = pci_ioaddr;
+		cs->hw.elsa.isac = pci_ioaddr +1;
+		cs->hw.elsa.hscx = pci_ioaddr +1;
+		cs->irq = pci_irq;
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig  = 0;
 		printk(KERN_INFO
-		       "Elsa: %s found at 0x%x IRQ %d\n",
-		       Elsa_Types[sp->subtyp],
-		       sp->cfg_reg,
-		       sp->irq);
+		       "Elsa: %s defined at 0x%x/0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.cfg,
+		       cs->irq);
+#else
+		printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
 	} else
 		return (0);
-#endif
 
-	switch (sp->subtyp) {
+	switch (cs->subtyp) {
 		case ELSA_PC:
-			bytecnt = 8;
-			break;
 		case ELSA_PCC8:
-			bytecnt = 8;
-			break;
-		case ELSA_PCFPRO:
-			bytecnt = 16;
-			break;
 		case ELSA_PCC16:
+		case ELSA_QS1000:
+		case ELSA_PCMCIA:
 			bytecnt = 8;
 			break;
+		case ELSA_PCFPRO:
 		case ELSA_PCF:
+		case ELSA_QS3000PCI:
 			bytecnt = 16;
 			break;
-		case ELSA_QS1000:
-			bytecnt = 8;
-			break;
-		case ELSA_PCMCIA:
-			bytecnt = 8;
+		case ELSA_QS1000PCI:
+			bytecnt = 2;
 			break;
 		default:
 			printk(KERN_WARNING
-			       "Unknown ELSA subtype %d\n", sp->subtyp);
+			       "Unknown ELSA subtype %d\n", cs->subtyp);
 			return (0);
 	}
-
-	if (check_region((sp->cfg_reg), bytecnt)) {
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && check_region(cs->hw.elsa.base, bytecnt)) {
 		printk(KERN_WARNING
 		       "HiSax: %s config port %x-%x already in use\n",
 		       CardType[card->typ],
-		       sp->cfg_reg,
-		       sp->cfg_reg + bytecnt);
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.base + bytecnt);
 		return (0);
 	} else {
-		request_region(sp->cfg_reg, bytecnt, "elsa isdn");
+		request_region(cs->hw.elsa.base, 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)) {
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+		if (check_region(cs->hw.elsa.cfg, 0x80)) {
 			printk(KERN_WARNING
-			       "Elsa: timer do not start\n");
-			release_io_elsa(card);
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.elsa.cfg,
+				cs->hw.elsa.cfg + 0x80);
+			release_region(cs->hw.elsa.base, bytecnt);
 			return (0);
+		} else {
+			request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci");
 		}
 	}
-	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);
+	cs->hw.elsa.tl.function = (void *) elsa_led_handler;
+	cs->hw.elsa.tl.data = (long) cs;
+	init_timer(&cs->hw.elsa.tl);
+	/* Teste Timer */
+	if (cs->hw.elsa.timer) {
+		byteout(cs->hw.elsa.trig, 0xff);
+		byteout(cs->hw.elsa.timer, 0);
+		if (!TimerRun(cs)) {
+			byteout(cs->hw.elsa.timer, 0);	/* 2. Versuch */
+			if (!TimerRun(cs)) {
+				printk(KERN_WARNING
+				       "Elsa: timer do not start\n");
+				release_io_elsa(cs);
+				return (0);
+			}
+		}
+		save_flags(flags);
+		sti();
+		HZDELAY(1);	/* wait >=10 ms */
+		restore_flags(flags);
+		if (TimerRun(cs)) {
+			printk(KERN_WARNING "Elsa: timer do not run down\n");
+			release_io_elsa(cs);
+			return (0);
+		}
+		printk(KERN_INFO "Elsa: timer OK; resetting card\n");
 	}
-	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);
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Elsa_card_msg;
+	reset_elsa(cs);
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID);
+		printk(KERN_INFO "Elsa: IPAC version %x\n", val);
+	} else {
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		ISACVersion(cs, "Elsa:");
+		if (HscxVersion(cs, "Elsa:")) {
+			printk(KERN_WARNING
+				"Elsa: wrong HSCX versions check IO address\n");
+			release_io_elsa(cs);
+			return (0);
+		}
 	}
-#endif
-
-#ifdef CONFIG_HISAX_ELSA_PCC
-	if (sp->subtyp == ELSA_PC) {
-		val = readitac(sp->cfg_reg, ITAC_SYS);
+	if (cs->subtyp == ELSA_PC) {
+		val = readitac(cs, 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);
+		writeitac(cs, ITAC_ISEN, 0);
+		writeitac(cs, ITAC_RFIE, 0);
+		writeitac(cs, ITAC_XFIE, 0);
+		writeitac(cs, ITAC_SCIE, 0);
+		writeitac(cs, 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.35/linux/drivers/isdn/hisax/elsa_ser.c linux/drivers/isdn/hisax/elsa_ser.c
--- v2.0.35/linux/drivers/isdn/hisax/elsa_ser.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/elsa_ser.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,748 @@
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+#define MAX_MODEM_BUF	256
+#define WAKEUP_CHARS	(MAX_MODEM_BUF/2)
+#define RS_ISR_PASS_LIMIT 256
+#define BASE_BAUD ( 1843200 / 16 )
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+//#define SERIAL_DEBUG_OPEN 1
+//#define SERIAL_DEBUG_INTR 1
+//#define SERIAL_DEBUG_FLOW 1
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_REG
+//#define SERIAL_DEBUG_REG 1
+
+#ifdef SERIAL_DEBUG_REG
+static u_char deb[32];
+const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"};
+const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"};
+#endif
+
+static char *MInit_1 = "AT&F&C1E0&D2\r\0";
+static char *MInit_2 = "ATL2M1S64=13\r\0";
+static char *MInit_3 = "AT+FCLASS=0\r\0";
+static char *MInit_4 = "ATV1S2=128X1\r\0";
+static char *MInit_5 = "AT\\V8\\N3\r\0";
+static char *MInit_6 = "ATL0M0&G0%E1\r\0";
+static char *MInit_7 = "AT%L1%M0%C3\r\0";
+
+static char *MInit_speed28800 = "AT%G0%B28800\r\0";
+
+static char *MInit_dialout = "ATs7=60 x1 d\r\0";
+static char *MInit_dialin = "ATs7=60 x1 a\r\0";
+
+
+static inline unsigned int serial_in(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"in   %s %02x",ModemIn[offset], val);
+	return(val);
+#else
+	return inb(cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inp  %s %02x",ModemIn[offset], val);
+#else
+	u_int val = inb_p(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inP  %s %02x",ModemIn[offset], val);
+#endif
+	return(val);
+#else
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	return inb(cs->hw.elsa.base + 8 + offset);
+#else
+	return inb_p(cs->hw.elsa.base + 8 + offset);
+#endif
+#endif
+}
+
+static inline void serial_out(struct IsdnCardState *cs, int offset, int value)
+{
+#ifdef SERIAL_DEBUG_REG
+	debugl1(cs,"out  %s %02x",ModemOut[offset], value);
+#endif
+	outb(value, cs->hw.elsa.base + 8 + offset);
+}
+
+static inline void serial_outp(struct IsdnCardState *cs, int offset,
+			       int value)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	debugl1(cs,"outp %s %02x",ModemOut[offset], value);
+#else
+	debugl1(cs,"outP %s %02x",ModemOut[offset], value);
+#endif
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	outb(value, cs->hw.elsa.base + 8 + offset);
+#else
+    	outb_p(value, cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct IsdnCardState *cs, int baud)
+{
+	int	quot = 0, baud_base;
+	unsigned cval, fcr = 0;
+	int	bits;
+	unsigned long	flags;
+
+
+	/* byte size and parity */
+	cval = 0x03; bits = 10;
+	/* Determine divisor based on baud rate */
+	baud_base = BASE_BAUD;
+	quot = baud_base / baud;
+	/* If the quotient is ever zero, default to 9600 bps */
+	if (!quot)
+		quot = baud_base / 9600;
+
+	/* Set up FIFO's */
+	if ((baud_base / quot) < 2400)
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+	else
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+	serial_outp(cs, UART_FCR, fcr);
+	/* CTS flow control flag and modem status interrupts */
+	cs->hw.elsa.IER &= ~UART_IER_MSI;
+	cs->hw.elsa.IER |= UART_IER_MSI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+
+	debugl1(cs,"modem quot=0x%x", quot);
+	save_flags(flags);
+	cli();
+	serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+	serial_outp(cs, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_outp(cs, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_outp(cs, UART_LCR, cval);		/* reset DLAB */
+	serial_inp(cs, UART_RX);
+	restore_flags(flags);
+}
+
+static int mstartup(struct IsdnCardState *cs)
+{
+	unsigned long flags;
+	int	retval=0;
+
+
+	save_flags(flags); cli();
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+
+	/*
+	 * At this point there's no way the LSR could still be 0xFF;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (serial_inp(cs, UART_LSR) == 0xff) {
+		retval = -ENODEV;
+		goto errout;
+	}
+	
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_inp(cs, UART_RX);
+	(void) serial_inp(cs, UART_IIR);
+	(void) serial_inp(cs, UART_MSR);
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	serial_outp(cs, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
+
+	cs->hw.elsa.MCR = 0;
+	cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);	/* enable interrupts */
+	
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void)serial_inp(cs, UART_LSR);
+	(void)serial_inp(cs, UART_RX);
+	(void)serial_inp(cs, UART_IIR);
+	(void)serial_inp(cs, UART_MSR);
+
+	cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0;
+	cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0;
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(cs, BASE_BAUD);
+	cs->hw.elsa.MFlag = 1;
+errout:
+	restore_flags(flags);
+	return retval;
+}
+
+/*
+ * 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 mshutdown(struct IsdnCardState *cs)
+{
+	unsigned long	flags;
+
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk(KERN_DEBUG"Shutting down serial ....");
+#endif
+	
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+
+	cs->hw.elsa.IER = 0;
+	serial_outp(cs, UART_IER, 0x00);	/* disable all intrs */
+	cs->hw.elsa.MCR &= ~UART_MCR_OUT2;
+	
+	/* disable break condition */
+	serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC);
+	
+	cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+	/* disable FIFO's */	
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+	serial_inp(cs, UART_RX);    /* read data port to reset things */
+	
+	restore_flags(flags);
+#ifdef SERIAL_DEBUG_OPEN
+	printk(" done\n");
+#endif
+}
+
+inline int
+write_modem(struct BCState *bcs) {
+	int ret=0;
+	struct IsdnCardState *cs = bcs->cs;
+	int count, len, fp, buflen;
+	long flags;
+	
+	if (!bcs->tx_skb)
+		return 0;
+	if (bcs->tx_skb->len <= 0)
+		return 0;
+	save_flags(flags);
+	cli();
+	buflen = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
+	len = MIN(buflen, bcs->tx_skb->len);		
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = MIN(len, MAX_MODEM_BUF - fp);
+	if (count < len) {
+		memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+		skb_pull(bcs->tx_skb, count);
+		cs->hw.elsa.transcnt += count;
+		ret = count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+	skb_pull(bcs->tx_skb, count);
+	cs->hw.elsa.transcnt += count;
+	ret += count;
+	
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+			cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+	restore_flags(flags);
+	return(ret);
+}
+
+inline void
+modem_fill(struct BCState *bcs) {
+		
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			write_modem(bcs);
+			return;
+		} else {
+			if (bcs->st->lli.l1writewakeup &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st,
+						bcs->hw.hscx.count);
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.hscx.count = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		write_modem(bcs);
+	} else {
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		hscx_sched_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+static inline void receive_chars(struct IsdnCardState *cs,
+				 int *status)
+{
+	unsigned char ch;
+	struct sk_buff *skb;
+
+	do {
+		ch = serial_in(cs, UART_RX);
+		if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF)
+			break;
+		cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch;
+#ifdef SERIAL_DEBUG_INTR
+		printk("DR%02x:%02x...", ch, *status);
+#endif
+		if (*status & (UART_LSR_BI | UART_LSR_PE |
+			       UART_LSR_FE | UART_LSR_OE)) {
+					
+#ifdef SERIAL_DEBUG_INTR
+			printk("handling exept....");
+#endif
+		}
+		*status = serial_inp(cs, UART_LSR);
+	} while (*status & UART_LSR_DR);
+	if (cs->hw.elsa.MFlag == 2) {
+		if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt)))
+			printk(KERN_WARNING "ElsaSER: receive out of memory\n");
+		else {
+			memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf, 
+				cs->hw.elsa.rcvcnt);
+			skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb);
+		}
+		hscx_sched_event(cs->hw.elsa.bcs, B_RCVBUFREADY);
+	} else {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt);
+		QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt);
+		debugl1(cs, tmp);
+	}
+	cs->hw.elsa.rcvcnt = 0;
+}
+
+static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done)
+{
+	int count;
+	
+	debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp, 
+		cs->hw.elsa.transcnt);
+	
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_out(cs, UART_IER, cs->hw.elsa.IER);
+		return;
+	}
+	count = 16;
+	do {
+		serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]);
+		if (cs->hw.elsa.transp >= MAX_MODEM_BUF)
+			cs->hw.elsa.transp=0;
+		if (--cs->hw.elsa.transcnt <= 0)
+			break;
+	} while (--count > 0);
+	if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2))
+		modem_fill(cs->hw.elsa.bcs);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+	if (intr_done)
+		*intr_done = 0;
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+}
+
+#if 0
+static inline void check_modem_status(struct IsdnCardState *cs)
+{
+	int	status;
+	struct async_struct *info = cs->hw.elsa.info;
+	struct	async_icount *icount;
+	
+	status = serial_inp(info, UART_MSR);
+
+	if (status & UART_MSR_ANY_DELTA) {
+		icount = &info->state->icount;
+		/* update input line counters */
+		if (status & UART_MSR_TERI)
+			icount->rng++;
+		if (status & UART_MSR_DDSR)
+			icount->dsr++;
+		if (status & UART_MSR_DDCD) {
+			icount->dcd++;
+		}
+		if (status & UART_MSR_DCTS)
+			icount->cts++;
+//		wake_up_interruptible(&info->delta_msr_wait);
+	}
+
+	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttys%d CD now %s...", info->line,
+		       (status & UART_MSR_DCD) ? "on" : "off");
+#endif		
+		if (status & UART_MSR_DCD)
+//			wake_up_interruptible(&info->open_wait);
+;
+		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("doing serial hangup...");
+#endif
+			if (info->tty)
+				tty_hangup(info->tty);
+		}
+	}
+#if 0
+	if (info->flags & ASYNC_CTS_FLOW) {
+		if (info->tty->hw_stopped) {
+			if (status & UART_MSR_CTS) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx start...");
+#endif
+				info->tty->hw_stopped = 0;
+				info->IER |= UART_IER_THRI;
+				serial_outp(info, UART_IER, info->IER);
+//				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				return;
+			}
+		} else {
+			if (!(status & UART_MSR_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx stop...");
+#endif
+				info->tty->hw_stopped = 1;
+				info->IER &= ~UART_IER_THRI;
+				serial_outp(info, UART_IER, info->IER);
+			}
+		}
+	}
+#endif 0
+}
+#endif
+
+static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs)
+{
+	int status, iir, msr;
+	int pass_counter = 0;
+	
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+	do {
+		status = serial_inp(cs, UART_LSR);
+		debugl1(cs,"rs LSR %02x", status);
+#ifdef SERIAL_DEBUG_INTR
+		printk("status = %x...", status);
+#endif
+		if (status & UART_LSR_DR)
+			receive_chars(cs, &status);
+		if (status & UART_LSR_THRE)
+			transmit_chars(cs, 0);
+		if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+			printk("rs_single loop break.\n");
+			break;
+		}
+		iir = serial_inp(cs, UART_IIR);
+		debugl1(cs,"rs IIR %02x", iir);
+		if ((iir & 0xf) == 0) {
+			msr = serial_inp(cs, UART_MSR);
+			debugl1(cs,"rs MSR %02x", msr);
+		}
+	} while (!(iir & UART_IIR_NO_INT));
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void hscx_l2l1(struct PStack *st, int pr, void *arg);
+
+void
+close_elsastate(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	modehscx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			if (bcs->mode != L1_MODE_MODEM)
+				kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		while ((skb = skb_dequeue(&bcs->rqueue))) {
+			dev_kfree_skb(skb, FREE_READ);
+		}
+		while ((skb = skb_dequeue(&bcs->squeue))) {
+			dev_kfree_skb(skb, FREE_WRITE);
+		}
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+void
+modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
+	int count, fp;
+	u_char *msg = buf;
+	long flags;
+	
+	if (!len)
+		return;
+	save_flags(flags);
+	cli();		
+	if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
+		restore_flags(flags);
+		return;
+	}
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = MIN(len, MAX_MODEM_BUF - fp);
+	if (count < len) {
+		memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+		cs->hw.elsa.transcnt += count;
+		msg += count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+	cs->hw.elsa.transcnt += count;
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+		cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+	restore_flags(flags);
+}
+
+void
+modem_set_init(struct IsdnCardState *cs) {
+	long flags;
+	int timeout;
+
+#define RCV_DELAY 20000	
+	save_flags(flags);
+	sti();
+	modem_write_cmd(cs, MInit_1, strlen(MInit_1));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_2, strlen(MInit_2));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_3, strlen(MInit_3));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_4, strlen(MInit_4));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY );
+	modem_write_cmd(cs, MInit_5, strlen(MInit_5));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_6, strlen(MInit_6));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_7, strlen(MInit_7));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	restore_flags(flags);
+}
+
+void
+modem_set_dial(struct IsdnCardState *cs, int outgoing) {
+	long flags;
+	int timeout;
+#define RCV_DELAY 20000	
+
+	save_flags(flags);
+	sti();
+	modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	if (outgoing)
+		modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout));
+	else
+		modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	restore_flags(flags);
+}
+
+void
+modem_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	if (pr == (PH_DATA | REQUEST)) {
+		save_flags(flags);
+		cli();
+		if (st->l1.bcs->tx_skb) {
+			skb_queue_tail(&st->l1.bcs->squeue, skb);
+			restore_flags(flags);
+		} else {
+			st->l1.bcs->tx_skb = skb;
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->hw.hscx.count = 0;
+			restore_flags(flags);
+			write_modem(st->l1.bcs);
+		}
+	} else if (pr == (PH_ACTIVATE | REQUEST)) {
+		test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+		st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+		set_arcofi(st->l1.bcs->cs, st->l1.bc);
+		mstartup(st->l1.bcs->cs);
+		modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
+		st->l1.bcs->cs->hw.elsa.MFlag=2;
+	} else if (pr == (PH_DEACTIVATE | REQUEST)) {
+		test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+		send_arcofi(st->l1.bcs->cs, ARCOFI_XOP_0, st->l1.bc, 0);
+		st->l1.bcs->cs->hw.elsa.MFlag=1;
+	} else {
+		printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr);
+	}
+}
+
+int
+setstack_elsa(struct PStack *st, struct BCState *bcs)
+{
+
+	bcs->channel = st->l1.bc;
+	switch (st->l1.mode) {
+		case L1_MODE_HDLC:
+		case L1_MODE_TRANS:
+			if (open_hscxstate(st->l1.hardware, bcs))
+				return (-1);
+			st->l2.l2l1 = hscx_l2l1;
+			break;
+		case L1_MODE_MODEM:
+			bcs->mode = L1_MODE_MODEM;
+			if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+				bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf;
+				skb_queue_head_init(&bcs->rqueue);
+				skb_queue_head_init(&bcs->squeue);
+			}
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->event = 0;
+			bcs->hw.hscx.rcvidx = 0;
+			bcs->tx_cnt = 0;
+			bcs->cs->hw.elsa.bcs = bcs;
+			st->l2.l2l1 = modem_l2l1;
+			break;
+	}
+	st->l1.bcs = bcs;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void
+init_modem(struct IsdnCardState *cs) {
+
+	cs->bcs[0].BC_SetStack = setstack_elsa;
+	cs->bcs[1].BC_SetStack = setstack_elsa;
+	cs->bcs[0].BC_Close = close_elsastate;
+	cs->bcs[1].BC_Close = close_elsastate;
+	if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.rcvbuf\n");
+		return;
+	}
+	if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.transbuf\n");
+		kfree(cs->hw.elsa.rcvbuf);
+		cs->hw.elsa.rcvbuf = NULL;
+		return;
+	}
+	if (mstartup(cs)) {
+		printk(KERN_WARNING "Elsa: problem startup modem\n");
+	}
+	modem_set_init(cs);
+}
+
+void
+release_modem(struct IsdnCardState *cs) {
+
+	cs->hw.elsa.MFlag = 0;
+	if (cs->hw.elsa.transbuf) {
+		if (cs->hw.elsa.rcvbuf) {
+			mshutdown(cs);
+			kfree(cs->hw.elsa.rcvbuf);
+			cs->hw.elsa.rcvbuf = NULL;
+		}
+		kfree(cs->hw.elsa.transbuf);
+		cs->hw.elsa.transbuf = NULL;
+	}
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c
--- v2.0.35/linux/drivers/isdn/hisax/fsm.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/fsm.c	Sun Nov 15 10:32:58 1998
@@ -1,4 +1,4 @@
-/* $Id: fsm.c,v 1.4 1997/04/06 22:56:42 keil Exp $
+/* $Id: fsm.c,v 1.4.2.5 1998/11/03 00:06:23 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
@@ -7,6 +7,28 @@
  *              Fritz Elfert
  *
  * $Log: fsm.c,v $
+ * Revision 1.4.2.5  1998/11/03 00:06:23  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.4.2.4  1998/05/27 18:05:21  keil
+ * HiSax 3.0
+ *
+ * Revision 1.4.2.3  1998/03/07 23:15:20  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
+ * Revision 1.4.2.2  1997/11/15 18:54:29  keil
+ * cosmetics
+ *
+ * Revision 1.4.2.1  1997/10/17 22:13:49  keil
+ * update to last hisax version
+ *
+ * Revision 1.6  1997/07/27 21:42:25  keil
+ * proof Fsm routines
+ *
+ * Revision 1.5  1997/06/26 11:10:05  keil
+ * Restart timer function added
+ *
  * Revision 1.4  1997/04/06 22:56:42  keil
  * Some cosmetic changes
  *
@@ -26,19 +48,24 @@
 
 #define FSM_TIMER_DEBUG 0
 
-void
+HISAX_INITFUNC(void
 FsmNew(struct Fsm *fsm,
-       struct FsmNode *fnlist, int fncount)
+       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;
+	fsm->jumpmatrix = (FSMFNPTR *)
+		kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
+	memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count);
+
+	for (i = 0; i < fncount; i++) 
+		if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) {
+			printk(KERN_ERR "FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n",
+				i,(long)fnlist[i].state,(long)fsm->state_count,
+				(long)fnlist[i].event,(long)fsm->event_count);
+		} else		
+			fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+				fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
 }
 
 void
@@ -50,26 +77,26 @@
 int
 FsmEvent(struct FsmInst *fi, int event, void *arg)
 {
-	void (*r) (struct FsmInst *, int, void *);
-	char str[80];
+	FSMFNPTR r;
 
-	r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+	if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) {
+		printk(KERN_ERR "FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
+			(long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count);
+		return(1);
+	}
+	r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
 	if (r) {
-		if (fi->debug) {
-			sprintf(str, "State %s Event %s",
+		if (fi->debug)
+			fi->printdebug(fi, "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",
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s no routine",
 				fi->fsm->strState[fi->state],
 				fi->fsm->strEvent[event]);
-			fi->printdebug(fi, str);
-		}
 		return (!0);
 	}
 }
@@ -77,25 +104,18 @@
 void
 FsmChangeState(struct FsmInst *fi, int newstate)
 {
-	char str[80];
-
 	fi->state = newstate;
-	if (fi->debug) {
-		sprintf(str, "ChangeState %s",
+	if (fi->debug)
+		fi->printdebug(fi, "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);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
 #endif
 	FsmEvent(ft->fi, ft->event, ft->arg);
 }
@@ -107,11 +127,8 @@
 	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);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
 #endif
 	init_timer(&ft->tl);
 }
@@ -120,11 +137,8 @@
 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);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
 #endif
 	del_timer(&ft->tl);
 }
@@ -135,11 +149,9 @@
 {
 
 #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);
-	}
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
+			(long) ft, millisec, where);
 #endif
 
 	if (ft->tl.next || ft->tl.prev) {
@@ -155,29 +167,22 @@
 	return 0;
 }
 
-int
-FsmTimerRunning(struct FsmTimer *ft)
-{
-	return (ft->tl.next != NULL);
-}
-
 void
-jiftime(char *s, long mark)
+FsmRestartTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
 {
-	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';
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (ft->tl.next || ft->tl.prev)
+		del_timer(&ft->tl);
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.c linux/drivers/isdn/hisax/hfc_2bds0.c
--- v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bds0.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,1229 @@
+/* $Id: hfc_2bds0.c,v 1.1.2.9 1998/11/03 00:06:24 keil Exp $
+ *
+ *  specific routines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hfc_2bds0.c,v $
+ * Revision 1.1.2.9  1998/11/03 00:06:24  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.8  1998/09/30 22:23:55  keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.1.2.7  1998/09/27 13:06:01  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.1.2.6  1998/06/27 22:54:07  keil
+ * make 16.3c working with 3.0
+ *
+ * Revision 1.1.2.5  1998/05/27 18:05:23  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.4  1998/04/08 21:54:35  keil
+ * Fix "ll_trans ..." message
+ *
+ * Revision 1.1.2.3  1998/04/04 21:59:20  keil
+ * Fixed B-channel access
+ *
+ * Revision 1.1.2.2  1998/01/27 22:40:35  keil
+ * fixed IRQ latency, B-channel selection and more
+ *
+ * Revision 1.1.2.1  1998/01/11 22:54:00  keil
+ * Teles 16.3c (HFC 2BDS0) first version
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+/*
+#define KDEBUG_DEF
+#include "kdebug.h"
+*/
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static void
+dummyf(struct IsdnCardState *cs, u_char * data, int size)
+{
+	printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n");
+}
+
+static inline u_char
+ReadReg(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		if (cs->hw.hfcD.cip != reg) { 
+			cs->hw.hfcD.cip = reg;
+			byteout(cs->hw.hfcD.addr | 1, reg);
+		}
+		ret = bytein(cs->hw.hfcD.addr);
+#if HFC_REG_DEBUG
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "t3c RD %02x %02x", reg, ret);
+#endif
+	} else
+		ret = bytein(cs->hw.hfcD.addr | 1);
+	return (ret);
+}
+
+static inline void
+WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	if (cs->hw.hfcD.cip != reg) { 
+		cs->hw.hfcD.cip = reg;
+		byteout(cs->hw.hfcD.addr | 1, reg);
+	}
+	if (data)
+		byteout(cs->hw.hfcD.addr, value);
+#if HFC_REG_DEBUG
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB))
+		debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
+#endif
+}
+
+/* Interface functions */
+
+static u_char
+readreghfcd(struct IsdnCardState *cs, u_char offset)
+{
+	return(ReadReg(cs, HFCD_DATA, offset));
+}
+
+static void
+writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	WriteReg(cs, HFCD_DATA, offset, value);
+}
+
+void
+set_cs_func(struct IsdnCardState *cs)
+{
+	cs->readisac = &readreghfcd;
+	cs->writeisac = &writereghfcd;
+	cs->readisacfifo = &dummyf;
+	cs->writeisacfifo = &dummyf;
+	cs->BC_Read_Reg = &ReadReg;
+	cs->BC_Write_Reg = &WriteReg;
+}
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+
+	while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: WaitForBusy timeout\n");
+	return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	long flags;
+	int to = 130;
+
+	while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) {
+		save_flags(flags);
+		sti();
+		udelay(1);
+		to--;
+		restore_flags(flags);
+	}
+	if (!to) 
+		printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n");
+	return (to);
+}
+
+static int
+SelFiFo(struct IsdnCardState *cs, u_char FiFo)
+{
+	u_char cip;
+	long flags;
+
+
+	if (cs->hw.hfcD.fifo == FiFo)
+		return(1);
+	save_flags(flags);
+	cli();
+	switch(FiFo) {
+		case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1;
+			break;
+		case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1;
+			break;
+		case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2;
+			break;
+		case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2;
+			break;
+		case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND;
+			break;
+		case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC;
+			break;
+		default:
+			restore_flags(flags);
+			debugl1(cs, "SelFiFo Error");
+			return(0);
+	}
+	cs->hw.hfcD.fifo = FiFo;
+	WaitNoBusy(cs);
+	cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0);
+	sti();
+	WaitForBusy(cs);
+	restore_flags(flags);
+	return(2);
+}
+static int
+GetFreeFifoBytes_B(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfcD.bfifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfcD.bfifosize;
+	s = bcs->cs->hw.hfcD.bfifosize - s;
+	return (s);
+}
+
+static int
+GetFreeFifoBytes_D(struct IsdnCardState *cs)
+{
+	int s;
+
+	if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2)
+		return (cs->hw.hfcD.dfifosize);
+	s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2];
+	if (s <= 0)
+		s += cs->hw.hfcD.dfifosize;
+	s = cs->hw.hfcD.dfifosize - s;
+	return (s);
+}
+
+static int
+ReadZReg(struct IsdnCardState *cs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(cs);
+	val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH);
+	WaitNoBusy(cs);
+	val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW);
+	return (val);
+}
+
+static void
+hfc_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static struct sk_buff
+*hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	long flags;
+	u_char stat, cip;
+	
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	save_flags(flags);
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		while (idx++ < count) {
+			cli();
+			WaitNoBusy(cs);
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+			sti();
+		}
+		skb = NULL;
+	} else if (count < 4) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		cli();
+		while ((idx++ < count) && WaitNoBusy(cs))
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+		skb = NULL;
+	} else if (!(skb = dev_alloc_skb(count - 3)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		SET_SKB_FREE(skb);
+		ptr = skb_put(skb, count - 3);
+		idx = 0;
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		cli();
+		while (idx < (count - 3)) {
+			cli();
+			if (!WaitNoBusy(cs))
+				break;
+			*ptr = ReadReg(cs,  HFCD_DATA_NODEB, cip);
+			sti();
+			ptr++;
+			idx++;
+		}
+		if (idx != count - 3) {
+			sti();
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb(skb, FREE_READ);
+			skb = NULL;
+		} else {
+			cli();
+			WaitNoBusy(cs);
+			chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+			WaitNoBusy(cs);
+			chksum += ReadReg(cs, HFCD_DATA, cip);
+			WaitNoBusy(cs);
+			stat = ReadReg(cs, HFCD_DATA, cip);
+			sti();
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+					bcs->channel, chksum, stat);
+			if (stat) {
+				debugl1(cs, "FIFO CRC error");
+				dev_kfree_skb(skb, FREE_READ);
+				skb = NULL;
+			}
+		}
+	}
+	sti();
+	WaitForBusy(cs);
+	cli();
+	WaitNoBusy(cs);
+	stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC |
+		HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	sti();
+	WaitForBusy(cs);
+	restore_flags(flags);
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+	save_flags(flags);
+	cli();
+	SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); 
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip);
+	WaitNoBusy(cs);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip);
+	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+	sti();
+ 	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	if (fcnt < 0)
+		fcnt += 32;
+	if (fcnt > 30) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo more as 30 frames");
+		restore_flags(flags);
+		return;
+	}
+	count = GetFreeFifoBytes_B(bcs);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d),%lx",
+			bcs->channel, bcs->tx_skb->len,
+			count, current->state);
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		restore_flags(flags);
+		return;
+	}
+	cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	idx = 0;
+	cli();
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	while (idx < bcs->tx_skb->len) {
+		cli();
+		if (!WaitNoBusy(cs))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx]);
+		sti();
+		idx++;
+	}
+	if (idx != bcs->tx_skb->len) {
+		sti();
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		bcs->tx_cnt -= bcs->tx_skb->len;
+		if (bcs->st->lli.l1writewakeup &&
+			(PACKET_NOACK != bcs->tx_skb->pkt_type))
+			bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
+		dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+		bcs->tx_skb = NULL;
+	}
+	WaitForBusy(cs);
+	cli();
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+	sti();
+	WaitForBusy(cs);
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	restore_flags(flags);
+	return;
+}
+
+static void
+hfc_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		hfc_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs,"send_data %d blocked", bcs->channel);
+}
+
+void
+main_rec_2bds0(struct BCState *bcs)
+{
+	long flags;
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, count = 5;
+	struct sk_buff *skb;
+
+	save_flags(flags);
+    Begin:
+	count--;
+	cli();
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs,"rec_data %d blocked", bcs->channel);
+		restore_flags(flags);
+		return;
+	}
+	SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f1 = ReadReg(cs, HFCD_DATA, cip);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = ReadReg(cs, HFCD_DATA, cip);
+	sti();
+	if (f1 != f2) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+		cli();
+		z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		sti();
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.bfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+			cli();
+			skb_queue_tail(&bcs->rqueue, skb);
+			sti();
+			hfc_sched_event(bcs, B_RCVBUFREADY);
+		}
+		rcnt = f1 -f2;
+		if (rcnt<0)
+			rcnt += 32;
+		if (rcnt>1)
+			receive = 1;
+		else
+			receive = 0;
+	} else
+		receive = 0;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;	
+	restore_flags(flags);
+	return;
+}
+
+void
+mode_2bs0(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCD bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcD.conn |= 0x18;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.conn |= 0x3;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfcD.ctmt |= 2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt |= 1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfcD.ctmt &= ~2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt &= ~1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+	}
+	WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+/*				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+*/				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+				break;
+			}
+			save_flags(flags);
+			cli();
+/*			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+*/			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			restore_flags(flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			mode_2bs0(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_2bs0(struct BCState *bcs)
+{
+	mode_2bs0(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+static void
+hfcd_bh(struct IsdnCardState *cs)
+{
+/*	struct PStack *stptr;
+*/
+	if (!cs)
+		return;
+#if 0	
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+#endif
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		switch (cs->ph_state) {
+			case (0):
+				l1_msg(cs, HW_RESET | INDICATION, NULL);
+				break;
+			case (3):
+				l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+				break;
+			case (8):
+				l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+				break;
+			case (6):
+				l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+				break;
+			case (7):
+				l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+				break;
+			default:
+				break;
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+void
+sched_event_D(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	queue_task(&cs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static
+int receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	long flags;
+	int idx;
+	int rcnt, z1, z2;
+	u_char stat, cip, f1, f2;
+	int chksum;
+	int count=5;
+	u_char *ptr;
+
+	save_flags(flags);
+	cli();
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		restore_flags(flags);
+		return(1);
+	}
+	SelFiFo(cs, 4 | HFCD_REC);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_REC;
+	WaitNoBusy(cs);
+	f1 = cs->readisac(cs, cip) & 0xf;
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+	WaitNoBusy(cs);
+	f2 = cs->readisac(cs, cip) & 0xf;
+	while ((f1 != f2) && count--) {
+		z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC);
+		z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC);
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.dfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+				f1, f2, z1, z2, rcnt);
+		sti();
+		idx = 0;
+		cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC;
+		if (rcnt > MAX_DFRAME_LEN + 3) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too large");
+			while (idx < rcnt) {
+				cli();
+				if (!(WaitNoBusy(cs)))
+					break;
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+				sti();
+				idx++;
+			}
+		} else if (rcnt < 4) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too small");
+			cli();
+			while ((idx++ < rcnt) && WaitNoBusy(cs))
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+		} else if ((skb = dev_alloc_skb(rcnt - 3))) {
+			SET_SKB_FREE(skb);
+			ptr = skb_put(skb, rcnt - 3);
+			while (idx < (rcnt - 3)) {
+				cli();
+				if (!(WaitNoBusy(cs)))
+					break;
+				*ptr = ReadReg(cs, HFCD_DATA_NODEB, cip);
+				sti();
+				idx++;
+				ptr++;
+			}
+			if (idx != (rcnt - 3)) {
+				sti();
+				debugl1(cs, "RFIFO D BUSY error");
+				printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n");
+				dev_kfree_skb(skb, FREE_READ);
+				skb = NULL;
+			} else {
+				cli();
+				WaitNoBusy(cs);
+				chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+				WaitNoBusy(cs);
+				chksum += ReadReg(cs, HFCD_DATA, cip);
+				WaitNoBusy(cs);
+				stat = ReadReg(cs, HFCD_DATA, cip);
+				sti();
+				if (cs->debug & L1_DEB_ISAC)
+					debugl1(cs, "empty_dfifo chksum %x stat %x",
+						chksum, stat);
+				if (stat) {
+					debugl1(cs, "FIFO CRC error");
+					dev_kfree_skb(skb, FREE_READ);
+					skb = NULL;
+				} else {
+					skb_queue_tail(&cs->rq, skb);
+					sched_event_D(cs, D_RCVBUFREADY);
+				}
+			}
+		} else
+			printk(KERN_WARNING "HFC: D receive out of memory\n");
+		sti();
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC;
+		cli();
+		WaitNoBusy(cs);
+		stat = ReadReg(cs, HFCD_DATA, cip);
+		sti();
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+		cli();
+		WaitNoBusy(cs);
+		f2 = cs->readisac(cs, cip) & 0xf;
+	}
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	restore_flags(flags);
+	return(1);
+} 
+
+static void
+hfc_fill_dfifo(struct IsdnCardState *cs)
+{
+	long flags;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	save_flags(flags);
+	cli();
+	SelFiFo(cs, 4 | HFCD_SEND);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND;
+	WaitNoBusy(cs);
+	cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	WaitNoBusy(cs);
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND;
+	cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND);
+	sti();
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
+			cs->hw.hfcD.f1, cs->hw.hfcD.f2,
+			cs->hw.hfcD.send[cs->hw.hfcD.f1]);
+	fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2;
+	if (fcnt < 0)
+		fcnt += 16;
+	if (fcnt > 14) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_Dfifo more as 14 frames");
+		restore_flags(flags);
+		return;
+	}
+	count = GetFreeFifoBytes_D(cs);
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo count(%ld/%d)",
+			cs->tx_skb->len, count);
+	if (count < cs->tx_skb->len) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfc_fill_Dfifo no fifo mem");
+		restore_flags(flags);
+		return;
+	}
+	cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND;
+	idx = 0;
+	cli();
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]);
+	while (idx < cs->tx_skb->len) {
+		cli();
+		if (!(WaitNoBusy(cs)))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]);
+		sti();
+		idx++;
+	}
+	if (idx != cs->tx_skb->len) {
+		sti();
+		debugl1(cs, "DFIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n");
+	}
+	WaitForBusy(cs);
+	cli();
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND);
+	dev_kfree_skb(cs->tx_skb, FREE_WRITE);
+	cs->tx_skb = NULL;
+	sti();
+	WaitForBusy(cs);
+	restore_flags(flags);
+	return;
+}
+
+static 
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void
+hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val)
+{
+       	u_char exval;
+       	struct BCState *bcs;
+	int count=15;
+	long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCD irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcD.int_m1;
+	if (val & 0x40) { /* TE state machine irq */
+		exval = cs->readisac(cs, HFCD_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->ph_state,
+				exval);
+		cs->ph_state = exval;
+		sched_event_D(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	while (val) {
+		save_flags(flags);
+		cli();
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcD.int_s1 |= val;
+			restore_flags(flags);
+			return;
+		}
+		if (cs->hw.hfcD.int_s1 & 0x18) {
+			exval = val;
+			val =  cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = exval;
+		}	
+		if (val & 0x08) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x08 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x10) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x10 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
+					} else {
+						hfc_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
+					} else {
+						hfc_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				sched_event_D(cs, D_CLEARBUSY);
+			if (cs->tx_skb)
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfc_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb(cs->tx_skb, FREE_WRITE);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfc_fill_dfifo irq blocked");
+				}
+			} else
+				sched_event_D(cs, D_XMTBUFREADY);
+		}
+      afterXPR:
+		if (cs->hw.hfcD.int_s1 && count--) {
+			val = cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFCD irq %x loop %d", val, 15-count);
+		} else
+			val = 0;
+		restore_flags(flags);
+	}
+}
+
+static void
+HFCD_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfc_fill_dfifo blocked");
+
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfc_fill_dfifo(cs);
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfc_fill_dfifo blocked");
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */
+			udelay(6);
+			cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			cs->hw.hfcD.mst_m &= ~HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			break;
+		case (HW_INFO3 | REQUEST):
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			break;
+#if 0
+		case (HW_TESTLOOP | REQUEST):
+			u_char val = 0;
+			if (1 & (int) arg)
+				val |= 0x0c;
+			if (2 & (int) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ISAC_SPCR, 0xa);
+					cs->writeisac(cs, ISAC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ISAC_SPCR, val);
+					cs->writeisac(cs, ISAC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ISAC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ISAC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ISAC_ADF1, 0x0);
+			}
+			break;
+#endif
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcd_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+void
+setstack_hfcd(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCD_l1hw;
+}
+
+static void
+hfc_dbusy_timer(struct IsdnCardState *cs)
+{
+#if 0
+	struct PStack *stptr;
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy");
+		test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+		stptr = cs->stlist;
+		
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+			stptr = stptr->next;
+		}
+	}
+#endif
+}
+
+__initfunc(unsigned int
+*init_send_hfcd(int cnt))
+{
+	int i, *send;
+
+	if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfcd.send\n");
+		return(NULL);
+	}
+	for (i = 0; i < cnt; i++)
+		send[i] = 0x1fff;
+	return(send);
+}
+
+__initfunc(void
+init2bds0(struct IsdnCardState *cs))
+{
+	cs->setstack_d = setstack_hfcd;
+	cs->dbusytimer.function = (void *) hfc_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	cs->tqueue.routine = (void *) (void *) hfcd_bh;
+	if (!cs->hw.hfcD.send)
+		cs->hw.hfcD.send = init_send_hfcd(16);
+	if (!cs->bcs[0].hw.hfc.send)
+		cs->bcs[0].hw.hfc.send = init_send_hfcd(32);
+	if (!cs->bcs[1].hw.hfc.send)
+		cs->bcs[1].hw.hfc.send = init_send_hfcd(32);
+	cs->BC_Send_Data = &hfc_send_data;
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_2bs0;
+	cs->bcs[1].BC_Close = close_2bs0;
+	mode_2bs0(cs->bcs, 0, 0);
+	mode_2bs0(cs->bcs + 1, 0, 1);
+}
+
+void
+release2bds0(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+	if (cs->hw.hfcD.send) {
+		kfree(cs->hw.hfcD.send);
+		cs->hw.hfcD.send = NULL;
+	}
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.h linux/drivers/isdn/hisax/hfc_2bds0.h
--- v2.0.35/linux/drivers/isdn/hisax/hfc_2bds0.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bds0.h	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,133 @@
+/* $Id: hfc_2bds0.h,v 1.1.2.2 1998/01/27 22:41:36 keil Exp $
+
+ *  specific defines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hfc_2bds0.h,v $
+ * Revision 1.1.2.2  1998/01/27 22:41:36  keil
+ * add set_cs_func()
+ *
+ * Revision 1.1.2.1  1998/01/11 22:54:02  keil
+ * Teles 16.3c (HFC 2BDS0) first version
+ *
+ *
+ */
+
+#define HFCD_CIRM  	0x18
+#define HFCD_CTMT	0x19
+#define HFCD_INT_M1  	0x1A
+#define HFCD_INT_M2  	0x1B
+#define HFCD_INT_S1  	0x1E
+#define HFCD_STAT  	0x1C
+#define HFCD_STAT_DISB  0x1D
+#define HFCD_STATES  	0x30
+#define HFCD_SCTRL  	0x31
+#define HFCD_TEST  	0x32
+#define HFCD_SQ  	0x34
+#define HFCD_CLKDEL  	0x37
+#define HFCD_MST_MODE	0x2E
+#define HFCD_CONN  	0x2F
+
+#define HFCD_FIFO	0x80
+#define HFCD_Z1		0x10
+#define HFCD_Z2		0x18
+#define HFCD_Z_LOW	0x00
+#define HFCD_Z_HIGH	0x04
+#define HFCD_F1_INC	0x12
+#define HFCD_FIFO_IN	0x16
+#define HFCD_F1		0x1a
+#define HFCD_F2		0x1e
+#define HFCD_F2_INC	0x22
+#define HFCD_FIFO_OUT	0x26
+#define HFCD_REC	0x01
+#define HFCD_SEND	0x00
+
+#define HFCB_FIFO	0x80
+#define HFCB_Z1		0x00
+#define HFCB_Z2		0x08
+#define HFCB_Z_LOW	0x00
+#define HFCB_Z_HIGH	0x04
+#define HFCB_F1_INC	0x28
+#define HFCB_FIFO_IN	0x2c
+#define HFCB_F1		0x30
+#define HFCB_F2		0x34
+#define HFCB_F2_INC	0x38
+#define HFCB_FIFO_OUT	0x3c
+#define HFCB_REC	0x01
+#define HFCB_SEND	0x00
+#define HFCB_B1		0x00
+#define HFCB_B2		0x02
+#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1)
+
+#define HFCD_STATUS	0
+#define HFCD_DATA	1
+#define HFCD_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFCD_BUSY	0x01
+#define HFCD_BUSY_NBUSY	0x04
+#define HFCD_TIMER_ELAP	0x10
+#define HFCD_STATINT	0x20
+#define HFCD_FRAMEINT	0x40
+#define HFCD_ANYINT	0x80
+
+/* CTMT (Write) */
+#define HFCD_CLTIMER 0x80
+#define HFCD_TIM25  0x00
+#define HFCD_TIM50  0x08
+#define HFCD_TIM400 0x10
+#define HFCD_TIM800 0x18
+#define HFCD_AUTO_TIMER 0x20
+#define HFCD_TRANSB2 0x02
+#define HFCD_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFCD_RESET  	0x08
+#define HFCD_MEM8K	0x10
+#define HFCD_INTA	0x01
+#define HFCD_INTB	0x02
+#define HFCD_INTC	0x03
+#define HFCD_INTD	0x04
+#define HFCD_INTE	0x05
+#define HFCD_INTF	0x06
+
+/* INT_M1;INT_S1 */
+#define HFCD_INTS_B1TRANS	0x01
+#define HFCD_INTS_B2TRANS	0x02
+#define HFCD_INTS_DTRANS	0x04
+#define HFCD_INTS_B1REC		0x08
+#define HFCD_INTS_B2REC		0x10
+#define HFCD_INTS_DREC		0x20
+#define HFCD_INTS_L1STATE	0x40
+#define HFCD_INTS_TIMER		0x80
+
+/* INT_M2 */
+#define HFCD_IRQ_ENABLE		0x08
+
+/* STATES */
+#define HFCD_LOAD_STATE		0x10
+#define HFCD_ACTIVATE		0x20
+#define HFCD_DO_ACTION		0x40
+
+/* HFCD_MST_MODE */
+#define HFCD_MASTER		0x01
+
+/* HFCD_SCTRL */
+#define SCTRL_B1_ENA		0x01
+#define SCTRL_B2_ENA		0x02
+#define SCTRL_LOW_PRIO		0x08
+#define SCTRL_SQ_ENA		0x10
+#define SCTRL_TEST		0x20
+#define SCTRL_NONE_CAP		0x40
+#define SCTRL_PWR_DOWN		0x80
+
+/* HFCD_TEST */
+#define HFCD_AUTO_AWAKE		0x01
+
+extern void main_irq_2bds0(struct BCState *bcs);
+extern void init2bds0(struct IsdnCardState *cs);
+extern void release2bds0(struct IsdnCardState *cs);
+extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val);
+extern void set_cs_func(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.c linux/drivers/isdn/hisax/hfc_2bs0.c
--- v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bs0.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,601 @@
+/* $Id: hfc_2bs0.c,v 1.1.2.8 1998/11/03 00:06:29 keil Exp $
+
+ *  specific routines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: hfc_2bs0.c,v $
+ * Revision 1.1.2.8  1998/11/03 00:06:29  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.7  1998/09/30 22:23:59  keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.1.2.6  1998/09/27 13:06:05  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.1.2.5  1998/05/27 18:05:27  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.4  1998/04/08 21:54:38  keil
+ * Fix "ll_trans ..." message
+ *
+ * Revision 1.1.2.3  1998/04/04 21:59:23  keil
+ * Fixed B-channel access
+ *
+ * Revision 1.1.2.2  1997/11/15 18:54:27  keil
+ * cosmetics
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:41  keil
+ * new files on 2.0
+ *
+ * Revision 1.1  1997/09/11 17:31:33  keil
+ * Common part for HFC 2BS0 based cards
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hfc_2bs0.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+	long flags;
+	u_char val;
+
+	save_flags(flags);
+	cli();
+	while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 |
+				      (cs->hw.hfc.cip & 3));
+		udelay(1);
+		to--;
+	}
+	restore_flags(flags);
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	int to = 125;
+
+	while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+int
+GetFreeFifoBytes(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfc.fifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfc.fifosize;
+	s = bcs->cs->hw.hfc.fifosize - s;
+	return (s);
+}
+
+int
+ReadZReg(struct BCState *bcs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(bcs->cs);
+	val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH);
+	WaitNoBusy(bcs->cs);
+	val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW);
+	return (val);
+}
+
+void
+hfc_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static void
+hfc_clear_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+	int idx, cnt;
+	int rcnt, z1, z2;
+	u_char cip, f1, f2;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_clear_fifo");
+	save_flags(flags);
+	cli();
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	cnt = 32;
+	while (((f1 != f2) || (z1 != z2)) && cnt--) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		if (rcnt)
+			rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		idx = 0;
+		while ((idx < rcnt) && WaitNoBusy(cs)) {
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (f1 != f2) {
+			WaitNoBusy(cs);
+			cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					HFC_CHANNEL(bcs->channel));
+			WaitForBusy(cs);
+		}
+		cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	}
+	restore_flags(flags);
+	return;
+}
+
+
+static struct sk_buff
+*
+hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	u_char stat, cip;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+		return (NULL);
+	}
+	if (count < 4) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+		return (NULL);
+	}
+	if (!(skb = dev_alloc_skb(count - 3)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		SET_SKB_FREE(skb);
+		ptr = skb_put(skb, count - 3);
+		idx = 0;
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx < count - 3) && WaitNoBusy(cs)) {
+			*ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (idx != count - 3) {
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb(skb, FREE_READ);
+			WaitNoBusy(cs);
+			stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					       HFC_CHANNEL(bcs->channel));
+			WaitForBusy(cs);
+			return (NULL);
+		}
+		WaitNoBusy(cs);
+		chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
+		WaitNoBusy(cs);
+		chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+				bcs->channel, chksum, stat);
+		if (stat) {
+			debugl1(cs, "FIFO CRC error");
+			dev_kfree_skb(skb, FREE_READ);
+			skb = NULL;
+		}
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+	}
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	save_flags(flags);
+	cli();
+	cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	if (fcnt < 0)
+		fcnt += 32;
+	if (fcnt > 30) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo more as 30 frames");
+		restore_flags(flags);
+		return;
+	}
+	count = GetFreeFifoBytes(bcs);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)",
+			bcs->channel, bcs->tx_skb->len,
+			count);
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		restore_flags(flags);
+		return;
+	}
+	cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	idx = 0;
+	while ((idx < bcs->tx_skb->len) && WaitNoBusy(cs))
+		cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	if (idx != bcs->tx_skb->len) {
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		count =  bcs->tx_skb->len;
+		bcs->tx_cnt -= count;
+		if (PACKET_NOACK == bcs->tx_skb->pkt_type)
+			count = -1;
+		dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+		bcs->tx_skb = NULL;
+		WaitForBusy(cs);
+		WaitNoBusy(cs);
+		cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
+		if (bcs->st->lli.l1writewakeup && (count >= 0))
+			bcs->st->lli.l1writewakeup(bcs->st, count);
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+	restore_flags(flags);
+	return;
+}
+
+void
+main_irq_hfc(struct BCState *bcs)
+{
+	long flags;
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, transmit, count = 5;
+	struct sk_buff *skb;
+
+	save_flags(flags);
+      Begin:
+	cli();
+	count--;
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	if (f1 != f2) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+		WaitForBusy(cs);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+/*              sti(); */
+		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+			skb_queue_tail(&bcs->rqueue, skb);
+			hfc_sched_event(bcs, B_RCVBUFREADY);
+		}
+		receive = 1;
+	} else
+		receive = 0;
+	restore_flags(flags);
+	udelay(1);
+	cli();
+	if (bcs->tx_skb) {
+		transmit = 1;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		hfc_fill_fifo(bcs);
+		if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+			transmit = 0;
+	} else {
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			transmit = 1;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hfc_fill_fifo(bcs);
+			if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+				transmit = 0;
+		} else {
+			transmit = 0;
+			hfc_sched_event(bcs, B_XMTBUFREADY);
+		}
+	}
+	restore_flags(flags);
+	if ((receive || transmit) && count)
+		goto Begin;
+	return;
+}
+
+void
+mode_hfc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc)
+				cs->hw.hfc.isac_spcr &= ~0x03;
+			else
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfc.ctmt |= 1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt |= 2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfc.ctmt &= ~1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt &= ~2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+	}
+	cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+	cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr);
+	if (mode)
+		hfc_clear_fifo(bcs);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+				break;
+			}
+			save_flags(flags);
+			cli();
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			restore_flags(flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			mode_hfc(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+
+void
+close_hfcstate(struct BCState *bcs)
+{
+	mode_hfc(bcs, 0, bcs->channel);
+	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+	test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hfc(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+__initfunc(void
+init_send(struct BCState *bcs))
+{
+	int i;
+
+	if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfc.send\n");
+		return;
+	}
+	for (i = 0; i < 32; i++)
+		bcs->hw.hfc.send[i] = 0x1fff;
+}
+
+__initfunc(void
+inithfc(struct IsdnCardState *cs))
+{
+	init_send(&cs->bcs[0]);
+	init_send(&cs->bcs[1]);
+	cs->BC_Send_Data = &hfc_fill_fifo;
+	cs->bcs[0].BC_SetStack = setstack_hfc;
+	cs->bcs[1].BC_SetStack = setstack_hfc;
+	cs->bcs[0].BC_Close = close_hfcstate;
+	cs->bcs[1].BC_Close = close_hfcstate;
+	mode_hfc(cs->bcs, 0, 0);
+	mode_hfc(cs->bcs + 1, 0, 0);
+}
+
+void
+releasehfc(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.h linux/drivers/isdn/hisax/hfc_2bs0.h
--- v2.0.35/linux/drivers/isdn/hisax/hfc_2bs0.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hfc_2bs0.h	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,65 @@
+/* $Id: hfc_2bs0.h,v 1.1.2.1 1997/10/17 22:10:43 keil Exp $
+
+ *  specific defines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hfc_2bs0.h,v $
+ * Revision 1.1.2.1  1997/10/17 22:10:43  keil
+ * new files on 2.0
+ *
+ * Revision 1.1  1997/09/11 17:31:34  keil
+ * Common part for HFC 2BS0 based cards
+ *
+ *
+ */
+
+#define HFC_CTMT	0xe0
+#define HFC_CIRM  	0xc0
+#define HFC_CIP		0x80
+#define HFC_Z1		0x00
+#define HFC_Z2		0x08
+#define HFC_Z_LOW	0x00
+#define HFC_Z_HIGH	0x04
+#define HFC_F1_INC	0x28
+#define HFC_FIFO_IN	0x2c
+#define HFC_F1		0x30
+#define HFC_F2		0x34
+#define HFC_F2_INC	0x38
+#define HFC_FIFO_OUT	0x3c
+#define HFC_B1          0x00
+#define HFC_B2		0x02
+#define HFC_REC		0x01
+#define HFC_SEND	0x00
+#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1)
+
+#define HFC_STATUS	0
+#define HFC_DATA	1
+#define HFC_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFC_BUSY	0x01
+#define HFC_TIMINT	0x02
+#define HFC_EXTINT	0x04
+
+/* CTMT (Write) */
+#define HFC_CLTIMER 0x10
+#define HFC_TIM50MS 0x08
+#define HFC_TIMIRQE 0x04
+#define HFC_TRANSB2 0x02
+#define HFC_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFC_RESET  	0x08
+#define HFC_MEM8K	0x10
+#define HFC_INTA	0x01
+#define HFC_INTB	0x02
+#define HFC_INTC	0x03
+#define HFC_INTD	0x04
+#define HFC_INTE	0x05
+#define HFC_INTF	0x06
+
+extern void main_irq_hfc(struct BCState *bcs);
+extern void inithfc(struct IsdnCardState *cs);
+extern void releasehfc(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- v2.0.35/linux/drivers/isdn/hisax/hisax.h	Wed Oct 15 15:25:02 1997
+++ linux/drivers/isdn/hisax/hisax.h	Sun Nov 15 10:32:58 1998
@@ -1,52 +1,96 @@
-/* $Id: hisax.h,v 1.13 1997/04/06 22:54:12 keil Exp $
+/* $Id: hisax.h,v 1.13.2.19 1998/11/05 21:11:17 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.13.2.19  1998/11/05 21:11:17  keil
+ * AVM PnP support
  *
- * Revision 1.12  1997/03/23 21:45:45  keil
- * Add support for ELSA PCMCIA
+ * Revision 1.13.2.18  1998/11/03 00:06:33  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.11  1997/02/11 01:36:02  keil
- * New Param structure
+ * Revision 1.13.2.17  1998/10/11 19:33:48  niemann
+ * Added new IPAC based cards.
+ * Code cleanup and simplified (sedlbauer.c)
  *
- * Revision 1.10  1997/02/09 00:23:52  keil
- * new interface handling, one interface per card
+ * Revision 1.13.2.16  1998/10/04 23:04:54  keil
+ * ISAR works now
  *
- * Revision 1.9  1997/01/27 23:18:44  keil
- * prototype for releasestack_isdnl3
+ * Revision 1.13.2.15  1998/09/30 22:28:04  keil
+ * more work for isar support
  *
- * Revision 1.8  1997/01/27 16:02:37  keil
- * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype
+ * Revision 1.13.2.14  1998/09/27 13:06:09  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
- * Revision 1.7  1997/01/21 22:22:14  keil
- * changes for 2.0; Elsa Quickstep support
+ * Revision 1.13.2.13  1998/08/25 14:01:30  calle
+ * Ported driver for AVM Fritz!Card PCI from the 2.1 tree.
+ * I could not test it.
  *
- * Revision 1.6  1997/01/04 13:48:28  keil
- * primitiv for MDL_REMOVE added
+ * Revision 1.13.2.12  1998/07/15 14:43:33  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
  *
- * Revision 1.5  1996/12/08 19:49:19  keil
- * Monitor channel support
+ * Revision 1.13.2.11  1998/05/27 18:05:30  keil
+ * HiSax 3.0
  *
- * Revision 1.4  1996/11/18 15:35:39  keil
- * some changes for ELSA cards
+ * Revision 1.13.2.10  1998/04/11 18:43:16  keil
+ * New cards
  *
- * Revision 1.3  1996/11/05 19:37:23  keil
- * using config.h
+ * Revision 1.13.2.9  1998/03/07 23:15:21  tsbogend
+ * made HiSax working on Linux/Alpha
  *
- * Revision 1.2  1996/10/27 22:21:52  keil
- * CallFlags for broadcast messages
+ * Revision 1.13.2.8  1998/02/11 14:23:10  keil
+ * support for Dr Neuhaus Niccy PnP and PCI
  *
- * Revision 1.1  1996/10/13 20:03:46  keil
- * Initial revision
+ * Revision 1.13.2.7  1998/02/09 11:21:22  keil
+ * Sedlbauer PCMCIA support from Marcus Niemann
  *
+ * Revision 1.13.2.6  1998/02/03 23:16:12  keil
+ * german AOC
  *
+ * Revision 1.13.2.5  1998/01/27 22:42:42  keil
+ * changes for new teles 16.3c and dynalink ---> asuscom
+ *
+ * Revision 1.13.2.4  1998/01/11 22:55:17  keil
+ * 16.3c support
+ *
+ * Revision 1.13.2.3  1997/11/27 12:31:59  keil
+ * Working netjet driver
+ *
+ * Revision 1.13.2.2  1997/11/15 18:55:43  keil
+ * New init, new cards
+ *
+ * Revision 1.13.2.1  1997/10/17 22:13:51  keil
+ * update to last hisax version
+ *
+ * Revision 2.6  1997/09/11 17:25:51  keil
+ * Add new cards
+ *
+ * Revision 2.5  1997/08/03 14:36:31  keil
+ * Implement RESTART procedure
+ *
+ * Revision 2.4  1997/07/31 19:25:20  keil
+ * PTP_DATA_LINK support
+ *
+ * Revision 2.3  1997/07/31 11:50:17  keil
+ * ONE TEI and FIXED TEI handling
+ *
+ * Revision 2.2  1997/07/30 17:13:02  keil
+ * more changes for 'One TEI per card'
+ *
+ * Revision 2.1  1997/07/27 21:45:13  keil
+ * new main structures
+ *
+ * Revision 2.0  1997/06/26 11:06:27  keil
+ * New card and L1 interface.
+ * Eicon.Diehl Diva and Dynalink IS64PH support
+ *
+ * old changes removed KKe
  *
  */
-#include <linux/module.h>
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/version.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -64,142 +108,113 @@
 #include <linux/wait.h>
 #include <linux/isdnif.h>
 #include <linux/tty.h>
+#include <linux/serial_reg.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
+#define REQUEST		0
+#define CONFIRM		1
+#define INDICATION	2
+#define RESPONSE	3
+
+#define HW_ENABLE	0x0000
+#define HW_RESET	0x0004
+#define HW_POWERUP	0x0008
+#define HW_ACTIVATE	0x0010
+#define HW_DEACTIVATE	0x0018
+#define HW_INFO2	0x0020
+#define HW_INFO3	0x0030
+#define HW_INFO4_P8	0x0040
+#define HW_INFO4_P10	0x0048
+#define HW_RSYNC	0x0060
+#define HW_TESTLOOP	0x0070
+#define CARD_RESET	0x00F0
+#define CARD_SETIRQ	0x00F1
+#define CARD_INIT	0x00F2
+#define CARD_RELEASE	0x00F3
+#define CARD_TEST	0x00F4
+#define CARD_AUX_IND	0x00F5
+#define CARD_LOAD_FIRM	0x00F6
+
+#define PH_ACTIVATE	0x0100
+#define PH_DEACTIVATE	0x0110
+#define PH_DATA		0x0120
+#define PH_PULL		0x0130
+#define PH_TESTLOOP	0x0140
+#define PH_PAUSE	0x0150
+#define MPH_ACTIVATE	0x0180
+#define MPH_DEACTIVATE	0x0190
+#define MPH_INFORMATION	0x01A0
+
+#define DL_ESTABLISH	0x0200
+#define DL_RELEASE	0x0210
+#define DL_DATA		0x0220
+#define DL_FLUSH	0x0224
+#define DL_UNIT_DATA	0x0230
+#define MDL_ASSIGN	0x0280
+#define MDL_REMOVE	0x0284
+#define MDL_ERROR	0x0288
+#define MDL_INFO_SETUP	0x02E0
+#define MDL_INFO_CONN	0x02E4
+#define MDL_INFO_REL	0x02E8
+
+#define CC_SETUP	0x0300
+#define CC_RESUME	0x0304
+#define CC_MORE_INFO	0x0310
+#define CC_IGNORE	0x0320
+#define CC_REJECT	0x0324
+#define CC_SETUP_COMPL	0x0330
+#define CC_PROCEEDING	0x0340
+#define CC_ALERTING	0x0344
+#define CC_CONNECT	0x0350
+#define CC_CHARGE	0x0354
+#define CC_DISCONNECT	0x0360
+#define CC_RELEASE	0x0368
+#define CC_SUSPEND	0x0370
+#define CC_T303		0x0383
+#define CC_T304		0x0384
+#define CC_T305		0x0385
+#define CC_T308_1	0x0388
+#define CC_T308_2	0x0389
+#define CC_T310		0x0390
+#define CC_T313		0x0393
+#define CC_T318		0x0398
+#define CC_T319		0x0399
+#define CC_NOSETUP_RSP	0x03E0
+#define CC_SETUP_ERR	0x03E1
+#define CC_SUSPEND_ERR	0x03E2
+#define CC_RESUME_ERR	0x03E3
+#define CC_CONNECT_ERR	0x03E4
+#define CC_RELEASE_ERR	0x03E5
+#define CC_DLRL		0x03F0
+#define CC_RESTART	0x03F4
 
-struct HscxIoctlArg {
-	int channel;
-	int mode;
-	int transbufsize;
-};
 
 #ifdef __KERNEL__
 
-#undef DEBUG_MAGIC
-
-#define MAX_DFRAME_LEN	3072
+#define MAX_DFRAME_LEN	260
+#define MAX_DFRAME_LEN_L1	300
 #define HSCX_BUFMAX	4096
 #define MAX_DATA_SIZE	(HSCX_BUFMAX - 4)
-#define MAX_DATA_MEM    (HSCX_BUFMAX * 2)
+#define MAX_DATA_MEM	(HSCX_BUFMAX + 64)
+#define RAW_BUFMAX	(((HSCX_BUFMAX*6)/5) + 5)
 #define MAX_HEADER_LEN	4
 #define MAX_WINDOW	8
+#define MAX_MON_FRAME	32
+#define MAX_DLOG_SPACE	2048
+#define MAX_BLOG_SPACE	256
+
+/* #define I4L_IRQ_FLAG SA_INTERRUPT */
+#define I4L_IRQ_FLAG    0
 
 /*
  * Statemachine
  */
+
+struct FsmInst;
+
+typedef void (* FSMFNPTR)(struct FsmInst *, int, void *);
+
 struct Fsm {
-	int *jumpmatrix;
+	FSMFNPTR *jumpmatrix;
 	int state_count, event_count;
 	char **strEvent, **strState;
 };
@@ -210,7 +225,7 @@
 	int debug;
 	void *userdata;
 	int userint;
-	void (*printdebug) (struct FsmInst *, char *);
+	void (*printdebug) (struct FsmInst *, char *, ...);
 };
 
 struct FsmNode {
@@ -226,73 +241,111 @@
 };
 
 struct L3Timer {
-	struct PStack *st;
+	struct l3_process *pc;
 	struct timer_list tl;
 	int event;
 };
 
+#define FLG_L1_ACTIVATING	1
+#define FLG_L1_ACTIVATED	2
+#define FLG_L1_DEACTTIMER	3
+#define FLG_L1_ACTTIMER		4
+#define FLG_L1_T3RUN		5
+#define FLG_L1_PULL_REQ		6
+
 struct Layer1 {
 	void *hardware;
-	int hscx;
+	struct BCState *bcs;
 	struct PStack **stlistp;
-	int act_state;
+	int Flags;
+	struct FsmInst l1m;
+	struct FsmTimer	timer;
 	void (*l1l2) (struct PStack *, int, void *);
-	void (*l1man) (struct PStack *, int, void *);
-	int hscxmode, hscxchannel, requestpull;
+	void (*l1hw) (struct PStack *, int, void *);
+	void (*l1tei) (struct PStack *, int, void *);
+	int mode, bc;
+	int delay;
 };
 
+#define GROUP_TEI	127
+#define TEI_SAPI	63
+#define CTRL_SAPI	0
+#define PACKET_NOACK	250
+
+/* Layer2 Flags */
+
+#define FLG_LAPB	0
+#define FLG_LAPD	1
+#define FLG_ORIG	2
+#define FLG_MOD128	3
+#define FLG_PEND_REL	4
+#define FLG_L3_INIT	5
+#define FLG_T200_RUN	6
+#define FLG_ACK_PEND	7
+#define FLG_REJEXC	8
+#define FLG_OWN_BUSY	9
+#define FLG_PEER_BUSY	10
+#define FLG_DCHAN_BUSY	11
+#define FLG_L1_ACTIV	12
+#define FLG_ESTAB_PEND	13
+#define FLG_PTP		14
+#define FLG_FIXED_TEI	15
+
 struct Layer2 {
-	int sap, tei, ces;
-	int extended, laptype;
-	int uihsize, ihsize;
+	int tei;
+	int sap;
+	int maxlen;
+	unsigned int flag;
 	int vs, va, vr;
-	struct sk_buff_head i_queue;
-	int window, orig;
-	int rejexp;
-	int debug;
-	struct sk_buff *windowar[MAX_WINDOW];
+	int rc;
+	int window;
 	int sow;
-	struct FsmInst l2m;
+	struct sk_buff *windowar[MAX_WINDOW];
+	struct sk_buff_head i_queue;
+	struct sk_buff_head ui_queue;
 	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 FsmInst l2m;
+	struct FsmTimer t200, t203;
+	int T200, N200, T203;
+	int debug;
+	char debug_id[16];
 };
 
 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;
+	struct FsmInst l3m;
+	struct sk_buff_head squeue;
+	struct l3_process *proc;
+	struct l3_process *global;
+	int N303;
 	int debug;
-	int channr;
+	char debug_id[8];
 };
 
-struct Layer4 {
+struct LLInterface {
 	void (*l4l3) (struct PStack *, int, void *);
 	void *userdata;
-	void (*l1writewakeup) (struct PStack *);
-	void (*l2writewakeup) (struct PStack *);
+	void (*l1writewakeup) (struct PStack *, int);
+	void (*l2writewakeup) (struct PStack *, int);
 };
 
+
 struct Management {
-	void (*manl1) (struct PStack *, int, void *);
-	void (*manl2) (struct PStack *, int, void *);
-	void (*teil2) (struct PStack *, int, void *);
+	int	ri;
+	struct FsmInst tei_m;
+	struct FsmTimer t202;
+	int T202, N202, debug;
+	void (*layer) (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
@@ -300,95 +353,368 @@
 	int spv;		/* SPV Flag */
 };
 
+
 struct PStack {
 	struct PStack *next;
 	struct Layer1 l1;
 	struct Layer2 l2;
 	struct Layer3 l3;
-	struct Layer4 l4;
+	struct LLInterface lli;
 	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 */
+struct l3_process {
+	int callref;
+	int state;
+	struct L3Timer timer;
+	int N303;
+	int debug;
+	struct Param para;
+	struct Channel *chan;
+	struct PStack *st;
+	struct l3_process *next;
+};
+
+struct hscx_hw {
+	int hscx;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+};
+
+struct isar_reg {
+	unsigned int Flags;
+	volatile u_char bstat;
+	volatile u_char iis;
+	volatile u_char cmsb;
+	volatile u_char clsb;
+	volatile u_char par[8];
+};
+
+struct isar_hw {
+	int dpath;
+	int rcvidx;
+	int txcnt;
+	int mml;
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+	struct isar_reg *reg;
+};
+
+struct hdlc_stat_reg {
+	u_char cmd  __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char fill __attribute__((packed));
+};
+
+struct hdlc_hw {
+	union {
+		u_int ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u_int stat;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+};
+
+struct hfcB_hw {
+	unsigned int *send;
+	int f1;
+	int f2;
+};
+
+struct tiger_hw {
+	u_int *send;
+	u_int *s_irq;
+	u_int *s_end;
+	u_int *sendp;
+	u_int *rec;
+	int free;
+	u_char *rcvbuf;
+	u_char *sendbuf;
+	u_char *sp;
+	int sendcnt;
+	u_int s_tot;
+	u_int r_bitcnt;
+	u_int r_tot;
+	u_int r_err;
+	u_int r_fcs;
+	u_char r_state;
+	u_char r_one;
+	u_char r_val;
+	u_char s_state;
+};
+
+struct amd7930_hw {
+	u_char *tx_buff;
+	u_char *rv_buff;
+	int rv_buff_in;
+	int rv_buff_out;
+	struct sk_buff *rv_skb;
+	struct hdlc_state *hdlc_state;
+	struct tq_struct tq_rcv;
+	struct tq_struct tq_xmt;
+};
+
+
+#define BC_FLG_INIT	1
+#define BC_FLG_ACTIV	2
+#define BC_FLG_BUSY	3
+#define BC_FLG_NOFRAME	4
+#define BC_FLG_HALF	5
+#define BC_FLG_EMPTY	6
+
+#define L1_MODE_NULL	0
+#define L1_MODE_TRANS	1
+#define L1_MODE_HDLC	2
+#define L1_MODE_MODEM	7
+
+struct BCState {
+	int channel;
+	int mode;
+	int Flag;
+	struct IsdnCardState *cs;
 	int tx_cnt;		/* B-Channel transmit counter */
-	int count;		/* Current skb sent count */
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
 	struct sk_buff_head rqueue;	/* B-Channel receive Queue */
-	struct sk_buff_head squeue;	/* B-Channel receive Queue */
+	struct sk_buff_head squeue;	/* B-Channel send Queue */
 	struct PStack *st;
+	u_char *blog;
+	struct timer_list transbusy;
 	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];
+	int  (*BC_SetStack) (struct PStack *, struct BCState *);
+	void (*BC_Close) (struct BCState *);
+	union {
+		struct hscx_hw hscx;
+		struct hdlc_hw hdlc;
+		struct isar_hw isar;
+		struct hfcB_hw hfc;
+		struct tiger_hw tiger;
+		struct amd7930_hw  amd7930;
+	} hw;
 };
 
 struct Channel {
-	struct PStack ds, is;
-	struct IsdnCardState *sp;
-	int hscx;
+	struct PStack *b_st, *d_st;
+	struct IsdnCardState *cs;
+	struct BCState *bcs;
 	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 l3_protocol;
 	int data_open;
-	int outcallref;
-	int impair;
+	struct l3_process *proc;
+	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
 	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;
+struct elsa_hw {
+	unsigned int base;
+	unsigned int cfg;
+	unsigned int ctrl;
+	unsigned int ale;
+	unsigned int isac;
+	unsigned int itac;
+	unsigned int hscx;
+	unsigned int trig;
+	unsigned int timer;
+	unsigned int counter;
+	unsigned int status;
+	struct timer_list tl;
+	unsigned int MFlag;
+	struct BCState *bcs;
+	u_char *transbuf;
+	u_char *rcvbuf;
+	unsigned int transp;
+	unsigned int rcvp;
+	unsigned int transcnt;
+	unsigned int rcvcnt;
+	u_char IER;
+	u_char FCR;
+	u_char LCR;
+	u_char MCR;
+	u_char ctrl_reg;
+};
+
+struct teles3_hw {
+	unsigned int cfg_reg;
+	signed   int isac;
+	signed   int hscx[2];
+	signed   int isacfifo;
+	signed   int hscxfifo[2];
+};
+
+struct teles0_hw {
 	unsigned int cfg_reg;
 	unsigned int membase;
+};
+
+struct avm_hw {
+	unsigned int cfg_reg;
 	unsigned int isac;
 	unsigned int hscx[2];
+	unsigned int isacfifo;
+	unsigned int hscxfifo[2];
 	unsigned int counter;
+};
+
+struct ix1_hw {
+	unsigned int cfg_reg;
+	unsigned int isac_ale;
+	unsigned int isac;
+	unsigned int hscx_ale;
+	unsigned int hscx;
+};
+
+struct diva_hw {
+	unsigned int cfg_reg;
+	unsigned int ctrl;
+	unsigned int isac_adr;
+	unsigned int isac;
+	unsigned int hscx_adr;
+	unsigned int hscx;
+	unsigned int status;
+	struct timer_list tl;
+	u_char ctrl_reg;
+};
+
+struct asus_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int u7;
+	unsigned int pots;
+};
+
+
+struct hfc_hw {
+	unsigned int addr;
+	unsigned int fifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	u_char isac_spcr;
+	struct timer_list timer;
+};
+
+struct sedl_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int reset_on;
+	unsigned int reset_off;
+	struct isar_reg isar;
+	unsigned int chip;
+	unsigned int bus;
+};
+
+struct spt_hw {
+	unsigned int cfg_reg;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned char res_irq;
+};
+
+struct mic_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+};
+
+struct njet_hw {
+	unsigned int base;
+	unsigned int isac;
+	unsigned int auxa;
+	unsigned char auxd;
+	unsigned char dmactrl;
+	unsigned char ctrl_reg;
+	unsigned char irqmask0;
+	unsigned char irqstat0;
+	unsigned char last_is0;
+};
+
+struct hfcD_hw {
+	unsigned int addr;
+	unsigned int bfifosize;
+	unsigned int dfifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+	unsigned char stat;
+	unsigned char fifo;
+	unsigned char f1;
+	unsigned char f2;
+	unsigned int *send;
+	struct timer_list timer;
+};
+
+#define HW_IOM1		0
+#define HW_IPAC		1
+#define HW_ISAR		2
+#define FLG_TWO_DCHAN	4
+#define FLG_L1_DBUSY	5
+#define FLG_DBUSY_TIMER 6
+#define FLG_LOCK_ATOMIC 7
+#define HW_MON0_RX_END	8
+#define HW_MON1_RX_END	9
+#define HW_MON0_TX_END	10
+#define HW_MON1_TX_END	11
+
+struct IsdnCardState {
+	unsigned char typ;
+	unsigned char subtyp;
+	int protocol;
+	unsigned int irq;
+	int HW_Flags;
+	int *busy_flag;
+	union {
+		struct elsa_hw elsa;
+		struct teles0_hw teles0;
+		struct teles3_hw teles3;
+		struct avm_hw avm;
+		struct ix1_hw ix1;
+		struct diva_hw diva;
+		struct asus_hw asus;
+		struct hfc_hw hfc;
+		struct sedl_hw sedl;
+		struct spt_hw spt;
+		struct mic_hw mic;
+		struct njet_hw njet;
+		struct hfcD_hw hfcD;
+		struct ix1_hw niccy;
+	} hw;
 	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 *);
+	u_char (*readisac) (struct IsdnCardState *, u_char);
+	void   (*writeisac) (struct IsdnCardState *, u_char, u_char);
+	void   (*readisacfifo) (struct IsdnCardState *, u_char *, int);
+	void   (*writeisacfifo) (struct IsdnCardState *, u_char *, int);
+	u_char (*BC_Read_Reg) (struct IsdnCardState *, int, u_char);
+	void   (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char);
+	void   (*BC_Send_Data) (struct BCState *);
+	int    (*cardmsg) (struct IsdnCardState *, int, void *);
 	struct Channel channel[2];
+	struct BCState bcs[2];
 	struct PStack *stlist;
 	u_char *rcvbuf;
 	int rcvidx;
@@ -396,16 +722,19 @@
 	int tx_cnt;
 	int event;
 	struct tq_struct tqueue;
-	int ph_active;
+	struct timer_list dbusytimer;
 	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 cardnr;
+	char *dlog;
 	int debug;
-	unsigned int CallFlags;
+	u_char *mon_tx;
+	u_char *mon_rx;
+	int mon_txp;
+	int mon_txc;
+	int mon_rxp;
+	u_char mocr;
+	void   (*setstack_d) (struct PStack *, struct IsdnCardState *);
 };
 
 #define  MON0_RX	1
@@ -413,128 +742,354 @@
 #define  MON0_TX	4
 #define  MON1_TX	8
 
+#define	 HISAX_MAX_CARDS	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_ELSA_PNP	7
 #define  ISDN_CTYPE_TELESPCMCIA	8
 #define  ISDN_CTYPE_IX1MICROR2	9
+#define  ISDN_CTYPE_ELSA_PCMCIA	10
+#define  ISDN_CTYPE_DIEHLDIVA	11
+#define  ISDN_CTYPE_ASUSCOM	12
+#define  ISDN_CTYPE_TELEINT	13
+#define  ISDN_CTYPE_TELES3C	14
+#define  ISDN_CTYPE_SEDLBAUER	15
+#define  ISDN_CTYPE_SPORTSTER	16
+#define  ISDN_CTYPE_MIC		17
+#define  ISDN_CTYPE_ELSA_PCI	18
+#define  ISDN_CTYPE_COMPAQ_ISA	19
+#define  ISDN_CTYPE_NETJET	20
+#define  ISDN_CTYPE_TELESPCI	21
+#define  ISDN_CTYPE_SEDLBAUER_PCMCIA	22
+#define  ISDN_CTYPE_AMD7930	23
+#define  ISDN_CTYPE_NICCY	24
+#define  ISDN_CTYPE_S0BOX	25
+#define  ISDN_CTYPE_A1_PCMCIA	26
+#define  ISDN_CTYPE_FRITZPCI	27
+#define  ISDN_CTYPE_SEDLBAUER_FAX     28
+
+#define  ISDN_CTYPE_COUNT	28
 
-#define  ISDN_CTYPE_COUNT	9
+#ifdef ISDN_CHIP_ISAC
+#undef ISDN_CHIP_ISAC
+#endif
+
+#ifndef __initfunc
+#define __initfunc(__arginit) __arginit
+#endif
+
+#ifndef __initdata
+#define __initdata
+#endif
+
+#define HISAX_INITFUNC(__arginit) __initfunc(__arginit)
+#define HISAX_INITDATA __initdata
 
 #ifdef	CONFIG_HISAX_16_0
 #define  CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #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)
+		     (1<< ISDN_CTYPE_TELESPCMCIA) | (1<< ISDN_CTYPE_COMPAQ_ISA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
 #define  CARD_TELES3  0
 #endif
 
+#ifdef	CONFIG_HISAX_TELESPCI
+#define  CARD_TELESPCI (1<< ISDN_CTYPE_TELESPCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELESPCI  0
+#endif
+
 #ifdef	CONFIG_HISAX_AVM_A1
 #define  CARD_AVM_A1 (1<< ISDN_CTYPE_A1)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
 #define  CARD_AVM_A1  0
 #endif
 
-#ifdef	CONFIG_HISAX_ELSA_PCC
-#define  CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000)
+#ifdef	CONFIG_HISAX_AVM_A1_PCMCIA
+#define  CARD_AVM_A1_PCMCIA (1<< ISDN_CTYPE_A1_PCMCIA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
-#define  CARD_ELSA  0
+#define  CARD_AVM_A1_PCMCIA  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"
+#ifdef	CONFIG_HISAX_FRITZPCI
+#define  CARD_FRITZPCI (1<< ISDN_CTYPE_FRITZPCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
-#undef CARD_ELSA
-#define CARD_ELSA (1<< ISDN_CTYPE_ELSA_QS1000)
+#define  CARD_FRITZPCI  0
 #endif
+
+#ifdef	CONFIG_HISAX_ELSA
+#define  CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \
+		   (1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#undef HISAX_INITFUNC
+#define HISAX_INITFUNC(__arginit) __arginit
+#undef HISAX_INITDATA
+#define HISAX_INITDATA
+#else
+#define  CARD_ELSA  0
 #endif
 
+
 #ifdef	CONFIG_HISAX_IX1MICROR2
 #define	CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
 #else
 #define CARD_IX1MICROR2 0
 #endif
 
+#ifdef  CONFIG_HISAX_DIEHLDIVA
+#define CARD_DIEHLDIVA (1 << ISDN_CTYPE_DIEHLDIVA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_DIEHLDIVA 0
+#endif
+
+#ifdef  CONFIG_HISAX_ASUSCOM
+#define CARD_ASUSCOM (1 << ISDN_CTYPE_ASUSCOM)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ASUSCOM 0
+#endif
+
+#ifdef  CONFIG_HISAX_TELEINT
+#define CARD_TELEINT (1 << ISDN_CTYPE_TELEINT)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELEINT 0
+#endif
+
+#ifdef  CONFIG_HISAX_SEDLBAUER
+#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) | ( 1 << ISDN_CTYPE_SEDLBAUER_FAX)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SEDLBAUER 0
+#endif
+
+#ifdef  CONFIG_HISAX_SPORTSTER
+#define CARD_SPORTSTER (1 << ISDN_CTYPE_SPORTSTER)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SPORTSTER 0
+#endif
+
+#ifdef  CONFIG_HISAX_MIC
+#define CARD_MIC (1 << ISDN_CTYPE_MIC)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_MIC 0
+#endif
+
+#ifdef  CONFIG_HISAX_NETJET
+#define CARD_NETJET (1 << ISDN_CTYPE_NETJET)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NETJET 0
+#endif
+
+#ifdef	CONFIG_HISAX_TELES3C
+#define  CARD_TELES3C (1<< ISDN_CTYPE_TELES3C)
+#else
+#define  CARD_TELES3C  0
+#endif
+
+#ifdef  CONFIG_HISAX_AMD7930
+#define CARD_AMD7930 (1 << ISDN_CTYPE_AMD7930)
+#else
+#define CARD_AMD7930 0
+#endif
+
+#ifdef	CONFIG_HISAX_NICCY
+#define	CARD_NICCY (1 << ISDN_CTYPE_NICCY)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NICCY 0
+#endif
+
+#ifdef	CONFIG_HISAX_S0BOX
+#define	CARD_S0BOX (1 << ISDN_CTYPE_S0BOX)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_S0BOX 0
+#endif
+
 #define  SUPORTED_CARDS  (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \
-			 | CARD_IX1MICROR2)
+			 | CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \
+			 | CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \
+			 | CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \
+			 | CARD_AVM_A1_PCMCIA | CARD_FRITZPCI\
+			 | CARD_NICCY | CARD_S0BOX | CARD_TELESPCI)
+
+#define TEI_PER_CARD 0
+
+#ifdef CONFIG_HISAX_1TR6
+#undef TEI_PER_CARD
+#define TEI_PER_CARD 1
+#endif
+
+#ifdef CONFIG_HISAX_EURO
+#undef TEI_PER_CARD
+#define TEI_PER_CARD 1
+#define HISAX_EURO_SENDCOMPLETE 1
+#define EXT_BEARER_CAPS 1
+#define HISAX_SEND_STD_LLC_IE 1
+#ifdef	CONFIG_HISAX_NO_SENDCOMPLETE
+#undef HISAX_EURO_SENDCOMPLETE
+#endif
+#ifdef	CONFIG_HISAX_NO_LLC
+#undef HISAX_SEND_STD_LLC_IE
+#endif
+#undef HISAX_DE_AOC
+#ifdef CONFIG_DE_AOC
+#define HISAX_DE_AOC 1
+#endif
+#endif
+
+/* L1 Debug */
+#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	L1_DEB_IPAC	        0x80
+#define	L1_DEB_RECEIVE_FRAME    0x100
+#define L1_DEB_MONITOR		0x200
+#define DEB_DLOG_HEX		0x400
+#define DEB_DLOG_VERBOSE	0x800
+
+#define L2FRAME_DEBUG
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
+#endif
 
 struct IsdnCard {
 	int typ;
 	int protocol;		/* EDSS1 or 1TR6 */
-	unsigned int para[3];
-	struct IsdnCardState *sp;
+	unsigned int para[4];
+	struct IsdnCardState *cs;
 };
 
+void init_bcstate(struct IsdnCardState *cs, int bc);
 
-#define LAPD 0
-#define LAPB 1
+void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs);
+unsigned int random_ri(void);
+void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
 
-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_l1_B(struct PStack *st);
 
-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 setstack_tei(struct PStack *st);
+void setstack_manager(struct PStack *st);
+
+void setstack_isdnl2(struct PStack *st, char *debug_id);
 void releasestack_isdnl2(struct PStack *st);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+
+void setstack_l3dc(struct PStack *st, struct Channel *chanp);
+void setstack_l3bc(struct PStack *st, struct Channel *chanp);
 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);
+int newcallref(void);
 
 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);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+	void *arg, int where);
+void FsmRestartTimer(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 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_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
 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 LogFrame(struct IsdnCardState *cs, u_char * p, int size);
+void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
 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__ */
+int discard_queue(struct sk_buff_head *q);
+#ifdef ISDN_CHIP_ISAC
+void setstack_isac(struct PStack *st, struct IsdnCardState *cs);
+#endif	/* ISDN_CHIP_ISAC */
+#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);
+int ll_run(struct IsdnCardState *cs);
+void ll_stop(struct IsdnCardState *cs);
 void CallcNew(void);
 void CallcFree(void);
-int CallcNewChan(struct IsdnCardState *csta);
-void CallcFreeChan(struct IsdnCardState *csta);
+int CallcNewChan(struct IsdnCardState *cs);
+void CallcFreeChan(struct IsdnCardState *cs);
+void Isdnl1New(void);
+void Isdnl1Free(void);
 void Isdnl2New(void);
 void Isdnl2Free(void);
-void init_tei(struct IsdnCardState *sp, int protocol);
-void release_tei(struct IsdnCardState *sp);
+void Isdnl3New(void);
+void Isdnl3Free(void);
+void init_tei(struct IsdnCardState *cs, int protocol);
+void release_tei(struct IsdnCardState *cs);
 char *HiSax_getrev(const char *revision);
+void TeiNew(void);
+void TeiFree(void);
+int certification_check(int output);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hscx.c linux/drivers/isdn/hisax/hscx.c
--- v2.0.35/linux/drivers/isdn/hisax/hscx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hscx.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,319 @@
+/* $Id: hscx.c,v 1.3.2.11 1998/11/05 21:14:01 keil Exp $
+
+ * hscx.c   HSCX specific routines
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: hscx.c,v $
+ * Revision 1.3.2.11  1998/11/05 21:14:01  keil
+ * minor fixes
+ *
+ * Revision 1.3.2.10  1998/11/03 00:06:37  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.3.2.9  1998/09/27 13:06:14  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.3.2.8  1998/09/15 15:25:04  keil
+ * Repair HSCX init
+ *
+ * Revision 1.3.2.7  1998/06/26 22:02:55  keil
+ * send flags between hdlc frames
+ *
+ * Revision 1.3.2.6  1998/06/09 18:26:32  keil
+ * PH_DEACTIVATE B-channel every time signaled to higher layer
+ *
+ * Revision 1.3.2.5  1998/05/27 18:05:34  keil
+ * HiSax 3.0
+ *
+ * Revision 1.3.2.4  1998/04/08 21:57:04  keil
+ * Fix "lltrans ..." message
+ * New init code to fix problems during init if S0 is allready activ
+ *
+ * Revision 1.3.2.3  1997/11/27 12:30:55  keil
+ * cosmetic changes
+ *
+ * Revision 1.3.2.2  1997/11/15 18:54:25  keil
+ * cosmetics
+ *
+ * Revision 1.3.2.1  1997/10/17 22:10:44  keil
+ * new files on 2.0
+ *
+ * Revision 1.3  1997/07/27 21:38:34  keil
+ * new B-channel interface
+ *
+ * Revision 1.2  1997/06/26 11:16:17  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hscx.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static char *HSCXVer[] HISAX_INITDATA =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+HISAX_INITFUNC(int
+HscxVersion(struct IsdnCardState *cs, char *s))
+{
+	int verA, verB;
+
+	verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf;
+	verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "%s HSCX version A: %s  B: %s\n", s,
+	       HSCXVer[verA], HSCXVer[verB]);
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf))
+		return (1);
+	else
+		return (0);
+}
+
+void
+modehscx(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hscx = bcs->hw.hscx.hscx;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, bc);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+		test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7);
+
+	/* Switch IOM 1 SSI */
+	if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0))
+		bc = 1 - bc;
+
+	if (bc == 0) {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : 0x2f);
+	} else {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x3);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x3);
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f);
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f);
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84);
+			break;
+		case (L1_MODE_TRANS):
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4);
+			break;
+		case (L1_MODE_HDLC):
+			cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+				test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d);
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c);
+			break;
+	}
+	if (mode)
+		cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41);
+	cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+hscx_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+void
+hscx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				st->l1.bcs->hw.hscx.count = 0;
+				restore_flags(flags);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->hw.hscx.count = 0;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			modehscx(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			modehscx(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_hscxstate(struct BCState *bcs)
+{
+	modehscx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+int
+open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for hscx.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hscx(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hscxstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hscx_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_hscx_ints(struct IsdnCardState *cs))
+{
+	int val, eval;
+
+	val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
+	debugl1(cs, "HSCX B ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
+		debugl1(cs, "HSCX B EXIR %x", eval);
+	}
+	if (val & 0x02) {
+		eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
+		debugl1(cs, "HSCX A EXIR %x", eval);
+	}
+	val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA);
+	debugl1(cs, "HSCX A ISTA %x", val);
+	val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
+	debugl1(cs, "HSCX B STAR %x", val);
+	val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
+	debugl1(cs, "HSCX A STAR %x", val);
+	/* disable all IRQ */
+	cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF);
+	cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF);
+}
+
+HISAX_INITFUNC(void
+inithscx(struct IsdnCardState *cs))
+{
+	cs->bcs[0].BC_SetStack = setstack_hscx;
+	cs->bcs[1].BC_SetStack = setstack_hscx;
+	cs->bcs[0].BC_Close = close_hscxstate;
+	cs->bcs[1].BC_Close = close_hscxstate;
+	cs->bcs[0].hw.hscx.hscx = 0;
+	cs->bcs[1].hw.hscx.hscx = 1;
+	modehscx(cs->bcs, 0, 0);
+	modehscx(cs->bcs + 1, 0, 0);
+}
+
+HISAX_INITFUNC(void
+inithscxisac(struct IsdnCardState *cs, int part))
+{
+	if (part & 1) {
+		clear_pending_isac_ints(cs);
+		clear_pending_hscx_ints(cs);
+		initisac(cs);
+		inithscx(cs);
+	}
+	if (part & 2) {
+		/* Reenable all IRQ */
+		cs->writeisac(cs, ISAC_MASK, 0);
+		cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
+		cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
+		/* RESET Receiver and Transmitter */
+		cs->writeisac(cs, ISAC_CMDR, 0x41);
+	}
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hscx.h linux/drivers/isdn/hisax/hscx.h
--- v2.0.35/linux/drivers/isdn/hisax/hscx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hscx.h	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,53 @@
+/* $Id: hscx.h,v 1.3.2.2 1998/04/08 21:57:30 keil Exp $
+
+ * hscx.h   HSCX specific defines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: hscx.h,v $
+ * Revision 1.3.2.2  1998/04/08 21:57:30  keil
+ * New init code to fix problems during init if S0 is allready activ
+ *
+ * Revision 1.3.2.1  1997/10/17 22:10:45  keil
+ * new files on 2.0
+ *
+ * Revision 1.3  1997/07/27 21:38:35  keil
+ * new B-channel interface
+ *
+ * Revision 1.2  1997/06/26 11:16:18  keil
+ * first version
+ *
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#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
+
+extern int HscxVersion(struct IsdnCardState *cs, char *s);
+extern void hscx_sched_event(struct BCState *bcs, int event);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void clear_pending_hscx_ints(struct IsdnCardState *cs);
+extern void inithscx(struct IsdnCardState *cs);
+extern void inithscxisac(struct IsdnCardState *cs, int part);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/hscx_irq.c linux/drivers/isdn/hisax/hscx_irq.c
--- v2.0.35/linux/drivers/isdn/hisax/hscx_irq.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hscx_irq.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,308 @@
+/* $Id: hscx_irq.c,v 1.5.2.5 1998/11/03 00:06:39 keil Exp $
+
+ * hscx_irq.c     low level b-channel stuff for Siemens HSCX
+ *
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ * This is an include file for fast inline IRQ stuff
+ *
+ * $Log: hscx_irq.c,v $
+ * Revision 1.5.2.5  1998/11/03 00:06:39  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.5.2.4  1998/09/27 13:06:16  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.5.2.3  1998/06/24 14:43:56  keil
+ * Fix recovery of TX IRQ loss
+ *
+ * Revision 1.5.2.2  1998/05/27 18:05:36  keil
+ * HiSax 3.0
+ *
+ * Revision 1.5.2.1  1997/10/17 22:10:46  keil
+ * new files on 2.0
+ *
+ * Revision 1.4  1997/08/15 17:48:02  keil
+ * cosmetic
+ *
+ * Revision 1.3  1997/07/27 21:38:36  keil
+ * new B-channel interface
+ *
+ * Revision 1.2  1997/06/26 11:16:19  keil
+ * first version
+ *
+ *
+ */
+
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(cs, hscx);
+	WRITEHSCX(cs, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+
+
+static void
+hscx_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+	long flags;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+		WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	save_flags(flags);
+	cli();
+	READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+hscx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	u_char *ptr;
+	long flags;
+
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_fill_fifo");
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->tx_skb->len;
+
+	waitforXFW(cs, bcs->hw.hscx.hscx);
+	save_flags(flags);
+	cli();
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + hscx;
+	struct sk_buff *skb;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	int count;
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = READHSCX(cs, hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX invalid frame");
+			if ((r & 0x40) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX RDO mode=%d",
+						bcs->mode);
+			if (!(r & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX CRC error");
+			WriteHSCXCMDR(cs, hscx, 0x80);
+		} else {
+			count = READHSCX(cs, hscx, HSCX_RBCL) & (
+				test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+			if (count == 0)
+				count = fifo_size;
+			hscx_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HSCX: receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		hscx_sched_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				SET_SKB_FREE(skb);
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			hscx_sched_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				hscx_fill_fifo(bcs);
+				return;
+			} else {
+				if (bcs->st->lli.l1writewakeup &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count);
+				dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+				bcs->hw.hscx.count = 0; 
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hscx_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			hscx_sched_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+	u_char exval;
+	struct BCState *bcs;
+
+	if (val & 0x01) {
+		bcs = cs->bcs + 1;
+		exval = READHSCX(cs, 1, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == 1)
+				hscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B EXIR %x", exval);
+	}
+	if (val & 0xf8) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B interrupt %x", val);
+		hscx_interrupt(cs, val, 1);
+	}
+	if (val & 0x02) {
+		bcs = cs->bcs;
+		exval = READHSCX(cs, 0, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == L1_MODE_TRANS)
+				hscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A EXIR %x", exval);
+	}
+	if (val & 0x04) {
+		exval = READHSCX(cs, 0, HSCX_ISTA);
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A interrupt %x", exval);
+		hscx_interrupt(cs, exval, 0);
+	}
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/ipac.h linux/drivers/isdn/hisax/ipac.h
--- v2.0.35/linux/drivers/isdn/hisax/ipac.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/ipac.h	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,36 @@
+/* $Id: ipac.h,v 1.1.2.2 1998/04/11 18:49:48 keil Exp $
+
+ * ipac.h   IPAC specific defines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: ipac.h,v $
+ * Revision 1.1.2.2  1998/04/11 18:49:48  keil
+ * add IPAC_ATX
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:48  keil
+ * new files on 2.0
+ *
+ *
+ *
+ */
+
+
+/* All Registers original Siemens Spec  */
+
+#define IPAC_CONF	0xC0
+#define IPAC_MASK	0xC1
+#define IPAC_ISTA	0xC1
+#define IPAC_ID		0xC2
+#define IPAC_ACFG	0xC3
+#define IPAC_AOE	0xC4
+#define IPAC_ARX	0xC5
+#define IPAC_ATX	0xC5
+#define IPAC_PITA1	0xC6
+#define IPAC_PITA2	0xC7
+#define IPAC_POTA1	0xC8
+#define IPAC_POTA2	0xC9
+#define IPAC_PCFG	0xCA
+#define IPAC_SCFG	0xCB
+#define IPAC_TIMR2	0xCC
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isac.c linux/drivers/isdn/hisax/isac.c
--- v2.0.35/linux/drivers/isdn/hisax/isac.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isac.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,669 @@
+/* $Id: isac.c,v 1.7.2.9 1998/11/03 00:06:41 keil Exp $
+
+ * isac.c   ISAC specific routines
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
+ * $Log: isac.c,v $
+ * Revision 1.7.2.9  1998/11/03 00:06:41  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.7.2.8  1998/09/27 13:06:18  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.7.2.7  1998/05/27 18:05:38  keil
+ * HiSax 3.0
+ *
+ * Revision 1.7.2.6  1998/04/08 21:57:31  keil
+ * New init code to fix problems during init if S0 is allready activ
+ *
+ * Revision 1.7.2.5  1998/03/07 23:15:24  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
+ * Revision 1.7.2.4  1998/02/09 11:24:06  keil
+ * New leased line support (Read README.HiSax!)
+ *
+ * Revision 1.7.2.3  1998/01/11 22:58:55  keil
+ * new setstack interface
+ *
+ * Revision 1.7.2.2  1997/11/15 18:54:23  keil
+ * cosmetics
+ *
+ * Revision 1.7.2.1  1997/10/17 22:10:49  keil
+ * new files on 2.0
+ *
+ * Revision 1.6  1997/08/15 17:47:08  keil
+ * avoid oops because a uninitialised timer
+ *
+ * Revision 1.5  1997/08/07 17:48:49  keil
+ * fix wrong parenthesis
+ *
+ * Revision 1.4  1997/07/30 17:11:59  keil
+ * fixed Timer3
+ *
+ * Revision 1.3  1997/07/27 21:37:40  keil
+ * T3 implemented; supervisor l1timer; B-channel TEST_LOOP
+ *
+ * Revision 1.2  1997/06/26 11:16:15  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 1
+
+static char *ISACVer[] HISAX_INITDATA =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+void
+ISACVersion(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readisac(cs, ISAC_RBCH);
+	printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+isac_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->ph_state) {
+		case (ISAC_IND_RS):
+		case (ISAC_IND_EI):
+			ph_command(cs, ISAC_CMD_DUI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+		case (ISAC_IND_DID):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (ISAC_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (ISAC_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (ISAC_IND_RSY):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (ISAC_IND_ARD):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (ISAC_IND_AI8):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+			break;
+		case (ISAC_IND_AI10):
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+isac_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		isac_new_ph(cs);		
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+	if (test_and_clear_bit(D_RX_MON0, &cs->event))
+		test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
+	if (test_and_clear_bit(D_RX_MON1, &cs->event))
+		test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
+	if (test_and_clear_bit(D_TX_MON0, &cs->event))
+		test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
+	if (test_and_clear_bit(D_TX_MON1, &cs->event))
+		test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
+}
+
+void
+isac_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "isac_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeisac(cs, ISAC_CMDR, 0x80);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	save_flags(flags);
+	cli();
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	save_flags(flags);
+	cli();
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "isac_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+void
+isac_sched_event(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	queue_task(&cs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+void
+isac_interrupt(struct IsdnCardState *cs, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+	long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ISAC interrupt %x", val);
+	if (val & 0x80) {	/* RME */
+		exval = cs->readisac(cs, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC RDO");
+			if (!(exval & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC CRC error");
+			cs->writeisac(cs, ISAC_CMDR, 0x80);
+		} else {
+			count = cs->readisac(cs, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(cs, count);
+			save_flags(flags);
+			cli();
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+			restore_flags(flags);
+		}
+		cs->rcvidx = 0;
+		isac_sched_event(cs, D_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(cs, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			isac_sched_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				isac_fill_fifo(cs);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb(cs->tx_skb, FREE_WRITE);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			isac_fill_fifo(cs);
+		} else
+			isac_sched_event(cs, D_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		exval = cs->readisac(cs, ISAC_CIR0);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC CIR0 %02X", exval );
+		if (exval & 2) {
+			cs->ph_state = (exval >> 2) & 0xf;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ph_state change %x", cs->ph_state);
+			isac_sched_event(cs, D_L1STATECHANGE);
+		}
+		if (exval & 1) {
+			exval = cs->readisac(cs, ISAC_CIR1);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ISAC CIR1 %02X", exval );
+		}
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = cs->readisac(cs, ISAC_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC EXIR %02x", exval);
+		if (exval & 0x04) {
+			v1 = cs->readisac(cs, ISAC_MOSR);
+			if (cs->debug & L1_DEB_MONITOR)
+				debugl1(cs, "ISAC MOSR %02x", v1);
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!cs->mon_rx) {
+					if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->mocr &= 0xf0;
+						cs->mocr |= 0x0a;
+						cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+						goto afterMONR0;
+					} else
+						cs->mon_rxp = 0;
+				}
+				if (cs->mon_rxp >= MAX_MON_FRAME) {
+					cs->mocr &= 0xf0;
+					cs->mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					cs->mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR0;
+				}
+				cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]);
+				if (cs->mon_rxp == 1) {
+					cs->mocr |= 0x04;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!cs->mon_rx) {
+					if (!(cs->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->mocr &= 0x0f;
+						cs->mocr |= 0xa0;
+						cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+						goto afterMONR1;
+					} else
+						cs->mon_rxp = 0;
+				}
+				if (cs->mon_rxp >= MAX_MON_FRAME) {
+					cs->mocr &= 0x0f;
+					cs->mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					cs->mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR1;
+				}
+				cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]);
+				cs->mocr |= 0x40;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				cs->mocr &= 0xf0;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				cs->mocr |= 0x0a;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
+			}
+			if (v1 & 0x40) {
+				cs->mocr &= 0x0f;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				cs->mocr |= 0xa0;
+				cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+				test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
+			}
+			if (v1 & 0x02) {
+				if ((!cs->mon_tx) || (cs->mon_txc && 
+					(cs->mon_txp >= cs->mon_txc) && 
+					!(v1 & 0x08))) {
+					cs->mocr &= 0xf0;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					cs->mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					if (cs->mon_txc &&
+						(cs->mon_txp >= cs->mon_txc))
+						test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
+					goto AfterMOX0;
+				}
+				if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) {
+					test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
+					goto AfterMOX0;
+				}
+				cs->writeisac(cs, ISAC_MOX0,
+					cs->mon_tx[cs->mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]);
+			}
+		      AfterMOX0:
+			if (v1 & 0x20) {
+				if ((!cs->mon_tx) || (cs->mon_txc && 
+					(cs->mon_txp >= cs->mon_txc) && 
+					!(v1 & 0x80))) {
+					cs->mocr &= 0x0f;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					cs->mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+					if (cs->mon_txc &&
+						(cs->mon_txp >= cs->mon_txc))
+						test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
+					goto AfterMOX1;
+				}
+				if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) {
+					test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
+					goto AfterMOX1;
+				}
+				cs->writeisac(cs, ISAC_MOX1,
+					cs->mon_tx[cs->mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]);
+			}
+		      AfterMOX1:
+#endif
+		}
+	}
+}
+
+static void
+ISAC_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	int  val;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				isac_fill_fifo(cs);
+			}
+			break;
+		case (PH_PULL |INDICATION):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			isac_fill_fifo(cs);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			if ((cs->ph_state == ISAC_IND_EI) ||
+				(cs->ph_state == ISAC_IND_DR) ||
+				(cs->ph_state == ISAC_IND_RS))
+			        ph_command(cs, ISAC_CMD_TIM);
+			else
+				ph_command(cs, ISAC_CMD_RS);
+			break;
+		case (HW_ENABLE | REQUEST):
+			ph_command(cs, ISAC_CMD_TIM);
+			break;
+		case (HW_INFO3 | REQUEST):
+			ph_command(cs, ISAC_CMD_AR8);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ISAC_SPCR, 0xa);
+					cs->writeisac(cs, ISAC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ISAC_SPCR, val);
+					cs->writeisac(cs, ISAC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ISAC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ISAC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ISAC_ADF1, 0x0);
+			}
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			discard_queue(&cs->rq);
+			discard_queue(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb(cs->tx_skb, FREE_WRITE);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				isac_sched_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isac_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_isac(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = ISAC_l1hw;
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int	val;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		if (cs->debug) {
+			debugl1(cs, "D-Channel Busy");
+			val = cs->readisac(cs, ISAC_RBCH);
+			if (val & ISAC_RBCH_XAC)
+				debugl1(cs, "ISAC XAC");
+			else
+				debugl1(cs, "ISAC No XAC");
+		}
+		test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+		stptr = cs->stlist;
+		
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+			stptr = stptr->next;
+		}
+	}
+}
+
+HISAX_INITFUNC(void
+initisac(struct IsdnCardState *cs))
+{
+	cs->tqueue.routine = (void *) (void *) isac_bh;
+	cs->setstack_d = setstack_isac;
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+  	cs->writeisac(cs, ISAC_MASK, 0xff);
+  	cs->mocr = 0xaa;
+	if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+		/* IOM 1 Mode */
+		cs->writeisac(cs, ISAC_ADF2, 0x0);
+		cs->writeisac(cs, ISAC_SPCR, 0xa);
+		cs->writeisac(cs, ISAC_ADF1, 0x2);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		cs->writeisac(cs, ISAC_ADF2, 0x80);
+		cs->writeisac(cs, ISAC_SQXR, 0x2f);
+		cs->writeisac(cs, ISAC_SPCR, 0x00);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+		cs->writeisac(cs, ISAC_TIMR, 0x00);
+		cs->writeisac(cs, ISAC_ADF1, 0x00);
+	}
+	ph_command(cs, ISAC_CMD_RS);
+	cs->writeisac(cs, ISAC_MASK, 0x0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_isac_ints(struct IsdnCardState *cs))
+{
+	int val, eval;
+
+	val = cs->readisac(cs, ISAC_STAR);
+	debugl1(cs, "ISAC STAR %x", val);
+	val = cs->readisac(cs, ISAC_MODE);
+	debugl1(cs, "ISAC MODE %x", val);
+	val = cs->readisac(cs, ISAC_ADF2);
+	debugl1(cs, "ISAC ADF2 %x", val);
+	val = cs->readisac(cs, ISAC_ISTA);
+	debugl1(cs, "ISAC ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->readisac(cs, ISAC_EXIR);
+		debugl1(cs, "ISAC EXIR %x", eval);
+	}
+	val = cs->readisac(cs, ISAC_CIR0);
+	debugl1(cs, "ISAC CIR0 %x", val);
+	cs->ph_state = (val >> 2) & 0xf;
+	isac_sched_event(cs, D_L1STATECHANGE);
+	/* Disable all IRQ */
+	cs->writeisac(cs, ISAC_MASK, 0xFF);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isac.h linux/drivers/isdn/hisax/isac.h
--- v2.0.35/linux/drivers/isdn/hisax/isac.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isac.h	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,84 @@
+/* $Id: isac.h,v 1.3.2.3 1998/05/27 18:05:41 keil Exp $
+
+ * isac.h   ISAC specific defines
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: isac.h,v $
+ * Revision 1.3.2.3  1998/05/27 18:05:41  keil
+ * HiSax 3.0
+ *
+ * Revision 1.3.2.2  1997/11/15 19:01:14  keil
+ * ipac changes
+ *
+ * Revision 1.3.2.1  1997/10/17 22:10:50  keil
+ * new files on 2.0
+ *
+ * Revision 1.3  1997/07/27 21:37:41  keil
+ * T3 implemented; supervisor l1timer; B-channel TEST_LOOP
+ *
+ * Revision 1.2  1997/06/26 11:16:16  keil
+ * first version
+ *
+ *
+ */
+
+
+/* All Registers original Siemens Spec  */
+
+#define ISAC_MASK 0x20
+#define ISAC_ISTA 0x20
+#define ISAC_STAR 0x21
+#define ISAC_CMDR 0x21
+#define ISAC_EXIR 0x24
+#define ISAC_ADF2 0x39
+#define ISAC_SPCR 0x30
+#define ISAC_ADF1 0x38
+#define ISAC_CIR0 0x31
+#define ISAC_CIX0 0x31
+#define ISAC_CIR1 0x33
+#define ISAC_CIX1 0x33
+#define ISAC_STCR 0x37
+#define ISAC_MODE 0x22
+#define ISAC_RSTA 0x27
+#define ISAC_RBCL 0x25
+#define ISAC_RBCH 0x2A
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+#define ISAC_MOSR 0x3a
+#define ISAC_MOCR 0x3a
+#define ISAC_MOR0 0x32
+#define ISAC_MOX0 0x32
+#define ISAC_MOR1 0x34
+#define ISAC_MOX1 0x34
+
+#define ISAC_RBCH_XAC 0x80
+
+#define ISAC_CMD_TIM	0x0
+#define ISAC_CMD_RS	0x1
+#define ISAC_CMD_SCZ	0x4
+#define ISAC_CMD_SSZ	0x2
+#define ISAC_CMD_AR8	0x8
+#define ISAC_CMD_AR10	0x9
+#define ISAC_CMD_ARL	0xA
+#define ISAC_CMD_DUI	0xF
+
+#define ISAC_IND_RS	0x1
+#define ISAC_IND_PU	0x7
+#define ISAC_IND_DR	0x0
+#define ISAC_IND_SD	0x2
+#define ISAC_IND_DIS	0x3
+#define ISAC_IND_EI	0x6
+#define ISAC_IND_RSY	0x4
+#define ISAC_IND_ARD	0x8
+#define ISAC_IND_TI	0xA
+#define ISAC_IND_ATI	0xB
+#define ISAC_IND_AI8	0xC
+#define ISAC_IND_AI10	0xD
+#define ISAC_IND_DID	0xF
+
+extern void ISACVersion(struct IsdnCardState *cs, char *s);
+extern void initisac(struct IsdnCardState *cs);
+extern void isac_interrupt(struct IsdnCardState *cs, u_char val);
+extern void clear_pending_isac_ints(struct IsdnCardState *cs);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c
--- v2.0.35/linux/drivers/isdn/hisax/isar.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isar.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,949 @@
+/* $Id: isar.c,v 1.1.2.4 1998/11/03 00:06:44 keil Exp $
+
+ * isar.c   ISAR (Siemens PSB 7110) specific routines
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.c,v $
+ * Revision 1.1.2.4  1998/11/03 00:06:44  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.3  1998/10/04 23:04:58  keil
+ * ISAR works now
+ *
+ * Revision 1.1.2.2  1998/09/30 22:28:07  keil
+ * more work for isar support
+ *
+ * Revision 1.1.2.1  1998/09/27 13:01:43  keil
+ * Start support for ISAR based cards
+ *
+ * Revision 1.1  1998/08/13 23:33:47  keil
+ * First version, only init
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBG_LOADFIRM	0
+#define DUMP_MBOXFRAME	2
+
+#define MIN(a,b) ((a<b)?a:b)
+
+void isar_setup(struct IsdnCardState *cs);
+
+static inline int
+waitforHIA(struct IsdnCardState *cs, int timeout)
+{
+
+	while ((cs->BC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) {
+		udelay(1);
+		timeout--;
+	}
+	if (!timeout)
+		printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n");
+	return(timeout);
+}
+
+
+int
+sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len,
+	u_char *msg)
+{
+	long flags;
+	int i;
+	
+	if (!waitforHIA(cs, 4000))
+		return(0);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len);
+#endif
+	save_flags(flags);
+	cli();
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg);
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len);
+	cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0);
+	if (msg && len) {
+		cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]);
+		for (i=1; i<len; i++)
+			cs->BC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = len;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "sendmbox cnt %d", len);
+				QuickHex(t, &msg[len-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_HIS, his);
+	restore_flags(flags);
+	waitforHIA(cs, 10000);
+	return(1);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg)
+{
+	int i;
+
+	cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0);
+	if (msg && ireg->clsb) {
+		msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX);
+		for (i=1; i < ireg->clsb; i++)
+			 msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = ireg->clsb;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb);
+				QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg)
+{
+	ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS);
+	ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H);
+	ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb,
+			ireg->clsb);
+#endif
+}
+
+int
+waitrecmsg(struct IsdnCardState *cs, u_char *len,
+	u_char *msg, int maxdelay)
+{
+	int timeout = 0;
+	long flags;
+	struct isar_reg *ir = cs->bcs[0].hw.isar.reg;
+	
+	
+	while((!(cs->BC_Read_Reg(cs, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) &&
+		(timeout++ < maxdelay))
+		udelay(1);
+	if (timeout >= maxdelay) {
+		printk(KERN_WARNING"isar recmsg IRQSTA timeout\n");
+		return(0);
+	}
+	save_flags(flags);
+	cli();
+	get_irq_infos(cs, ir);
+	rcv_mbox(cs, ir, msg);
+	*len = ir->clsb;
+	restore_flags(flags);
+	return(1);
+}
+
+int
+ISARVersion(struct IsdnCardState *cs, char *s)
+{
+	int ver;
+	u_char msg[] = ISAR_MSG_HWVER;
+	u_char tmp[64];
+	u_char len;
+	int debug;
+
+	cs->cardmsg(cs, CARD_RESET,  NULL);
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	debug = cs->debug;
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+	if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg))
+		return(-1);
+	if (!waitrecmsg(cs, &len, tmp, 100000))
+		 return(-2);
+	cs->debug = debug;
+	if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) {
+		if (len == 1) {
+			ver = tmp[0] & 0xf;
+			printk(KERN_INFO "%s ISAR version %d\n", s, ver);
+			return(ver);
+		}
+		return(-3);
+	}
+	return(-4);
+}
+
+int
+isar_load_firmware(struct IsdnCardState *cs, u_char *buf)
+{
+	int ret, size, cnt, debug;
+	u_char len, nom, noc;
+	u_short sadr, left, *sp;
+	u_char *p = buf;
+	u_char *msg, *tmpmsg, *mp, tmp[64];
+	long flags;
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	
+	struct {u_short sadr;
+		u_short len;
+		u_short d_key;
+	} blk_head;
+		
+#define	BLK_HEAD_SIZE 6
+	if (1 != (ret = ISARVersion(cs, "Testing"))) {
+		printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret);
+		return(1);
+	}
+	debug = cs->debug;
+#if DBG_LOADFIRM<2
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+#endif
+	printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf);
+	if ((ret = verify_area(VERIFY_READ, (void *) p, sizeof(int)))) {
+		printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+		return ret;
+	}
+	if ((ret = copy_from_user(&size, p, sizeof(int)))) {
+		printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+		return ret;
+	}
+	p += sizeof(int);
+	printk(KERN_DEBUG"isar_load_firmware size: %d\n", size);
+	if ((ret = verify_area(VERIFY_READ, (void *) p, size))) {
+		printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+		return ret;
+	}
+	cnt = 0;
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	if (!(msg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no buffer\n");
+		return (1);
+	}
+	if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no tmp buffer\n");
+		kfree(msg);
+		return (1);
+	}
+	while (cnt < size) {
+		if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) {
+			printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+			goto reterror;
+		}
+		cnt += BLK_HEAD_SIZE;
+		p += BLK_HEAD_SIZE;
+		printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n",
+			blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+		sadr = blk_head.sadr;
+		left = blk_head.len;
+		if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) {
+			printk(KERN_ERR"isar sendmsg dkey failed\n");
+			ret = 1;goto reterror;
+		}
+		if (!waitrecmsg(cs, &len, tmp, 100000)) {
+			printk(KERN_ERR"isar waitrecmsg dkey failed\n");
+			ret = 1;goto reterror;
+		}
+		if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) {
+			printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n",
+				ireg->iis, ireg->cmsb, len);
+			ret = 1;goto reterror;
+		}
+		while (left>0) {
+			noc = MIN(126, left);
+			nom = 2*noc;
+			mp  = msg;
+			*mp++ = sadr / 256;
+			*mp++ = sadr % 256;
+			left -= noc;
+			*mp++ = noc;
+			if ((ret = copy_from_user(tmpmsg, p, nom))) {
+				printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+				goto reterror;
+			}
+			p += nom;
+			cnt += nom;
+			nom += 3;
+			sp = (u_short *)tmpmsg;
+#if DBG_LOADFIRM
+			printk(KERN_DEBUG"isar: load %3d words at %04x\n",
+				 noc, sadr);
+#endif
+			sadr += noc;
+			while(noc) {
+				*mp++ = *sp / 256;
+				*mp++ = *sp % 256;
+				sp++;
+				noc--;
+			}
+			if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) {
+				printk(KERN_ERR"isar sendmsg prog failed\n");
+				ret = 1;goto reterror;
+			}
+			if (!waitrecmsg(cs, &len, tmp, 100000)) {
+				printk(KERN_ERR"isar waitrecmsg prog failed\n");
+				ret = 1;goto reterror;
+			}
+			if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) {
+				printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n",
+					ireg->iis, ireg->cmsb, len);
+				ret = 1;goto reterror;
+			}
+		}
+		printk(KERN_DEBUG"isar firmware block %5d words loaded\n",
+			blk_head.len);
+	}
+	msg[0] = 0xff;
+	msg[1] = 0xfe;
+	ireg->bstat = 0;
+	if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) {
+		printk(KERN_ERR"isar sendmsg start dsp failed\n");
+		ret = 1;goto reterror;
+	}
+	if (!waitrecmsg(cs, &len, tmp, 100000)) {
+		printk(KERN_ERR"isar waitrecmsg start dsp failed\n");
+		ret = 1;goto reterror;
+	}
+	if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) {
+		printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n",
+			ireg->iis, ireg->cmsb, len);
+		ret = 1;goto reterror;
+	} else
+		printk(KERN_DEBUG"isar start dsp success\n");
+	/* NORMAL mode entered */
+	/* Enable IRQs of ISAR */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA);
+	save_flags(flags);
+	sti();
+	cnt = 1000; /* max 1s */
+	while ((!ireg->bstat) && cnt) {
+		udelay(1000);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no general status event received\n");
+		ret = 1;goto reterrflg;
+	} else {
+		printk(KERN_DEBUG"isar general status event %x\n",
+			ireg->bstat);
+	}
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+		printk(KERN_ERR"isar sendmsg self tst failed\n");
+		ret = 1;goto reterrflg;
+	}
+	cnt = 1000; /* max 10 ms */
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no self tst response\n");
+		ret = 1;goto reterrflg;
+	} else if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1)
+		&& (ireg->par[0] == 0)) {
+		printk(KERN_DEBUG"isar selftest OK\n");
+	} else {
+		printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n",
+			ireg->cmsb, ireg->clsb, ireg->par[0]);
+		ret = 1;goto reterror;
+	}
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+		printk(KERN_ERR"isar RQST SVN failed\n");
+		ret = 1;goto reterror;
+	}
+	cnt = 10000; /* max 100 ms */
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no SVN response\n");
+		ret = 1;goto reterrflg;
+	} else {
+		if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1))
+			printk(KERN_DEBUG"isar software version %#x\n",
+				ireg->par[0]);
+		else {
+			printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n",
+				ireg->cmsb, ireg->clsb, cnt);
+			ret = 1;goto reterrflg;
+		}
+	}
+	cs->debug = debug;
+	isar_setup(cs);
+	ret = 0;
+reterrflg:
+	restore_flags(flags);
+reterror:
+	cs->debug = debug;
+	if (ret)
+		/* disable ISAR IRQ */
+		cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	kfree(msg);
+	kfree(tmpmsg);
+	return(ret);
+}
+
+void
+isar_sched_event(struct BCState *bcs, int event)
+{
+	bcs->event |= 1 << event;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+static inline void
+isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct isar_reg *ireg = bcs->hw.isar.reg;
+	
+	if (!ireg->clsb) {
+		debugl1(cs, "isar zero len frame");
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		return;
+	}
+	switch (bcs->mode) {
+	case L1_MODE_NULL:
+		debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	case L1_MODE_TRANS:
+		if ((skb = dev_alloc_skb(ireg->clsb))) {
+			SET_SKB_FREE(skb);
+			rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb));
+			skb_queue_tail(&bcs->rqueue, skb);
+			isar_sched_event(bcs, B_RCVBUFREADY);
+		} else {
+			printk(KERN_WARNING "HiSax: skb out of memory\n");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		}
+		break;
+	case L1_MODE_HDLC:
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD)
+				bcs->hw.isar.rcvidx = 0;
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
+						bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2)))
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				else {
+					SET_SKB_FREE(skb);
+					memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2),
+						bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2);
+					skb_queue_tail(&bcs->rqueue, skb);
+					isar_sched_event(bcs, B_RCVBUFREADY);
+				}
+			}
+		}
+		break;
+	default:
+		printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	}
+}
+
+void
+isar_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count;
+	u_char msb;
+	u_char *ptr;
+	long flags;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "isar_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+	if (!(bcs->hw.isar.reg->bstat & 
+		(bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+		return;
+	if (bcs->tx_skb->len > bcs->hw.isar.mml) {
+		msb = 0;
+		count = bcs->hw.isar.mml;
+	} else {
+		count = bcs->tx_skb->len;
+		msb = HDLC_FED;
+	}
+	if (!bcs->hw.isar.txcnt)
+		msb |= HDLC_FST;
+	save_flags(flags);
+	cli();
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.isar.txcnt += count;
+	switch (bcs->mode) {
+	case L1_MODE_NULL:
+		printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+		break;
+	case L1_MODE_TRANS:
+		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+			0, count, ptr)) {
+			if (cs->debug)
+				debugl1(cs, "isar bin data send dp%d failed",
+					bcs->hw.isar.dpath);
+		}
+		break;
+	case L1_MODE_HDLC:
+		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+			msb, count, ptr)) {
+			if (cs->debug)
+				debugl1(cs, "isar hdlc data send dp%d failed",
+					bcs->hw.isar.dpath);
+		}
+		break;
+	default:
+		printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode);
+		break;
+	}
+	restore_flags(flags);
+}
+
+inline
+struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath)
+{
+	if ((!dpath) || (dpath == 3))
+		return(NULL);
+	if (cs->bcs[0].hw.isar.dpath == dpath)
+		return(&cs->bcs[0]);
+	if (cs->bcs[1].hw.isar.dpath == dpath)
+		return(&cs->bcs[1]);
+	return(NULL);
+}
+
+inline void
+send_frames(struct BCState *bcs)
+{
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			isar_fill_fifo(bcs);
+			return;
+		} else {
+			if (bcs->st->lli.l1writewakeup &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt);
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->hw.isar.txcnt = 0; 
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.isar.txcnt = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		isar_fill_fifo(bcs);
+	} else {
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		isar_sched_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+inline void
+check_send(struct IsdnCardState *cs, u_char rdm)
+{
+	struct BCState *bcs;
+	
+	if (rdm & BSTAT_RDM1) {
+		if ((bcs = sel_bcs_isar(cs, 1))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	if (rdm & BSTAT_RDM2) {
+		if ((bcs = sel_bcs_isar(cs, 2))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	
+}
+
+static char debbuf[64];
+
+void
+isar_int_main(struct IsdnCardState *cs)
+{
+	long flags;
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	struct BCState *bcs;
+
+	save_flags(flags);
+	cli();
+	get_irq_infos(cs, ireg);
+	switch (ireg->iis & ISAR_IIS_MSCMSD) {
+		case ISAR_IIS_RDATA:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				isar_rcv_frame(cs, bcs);
+			} else {
+				debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_GSTEV:
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			ireg->bstat |= ireg->cmsb;
+			check_send(cs, ireg->cmsb);
+			break;
+		case ISAR_IIS_BSTEV:
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "Buffer STEV dpath%d msb(%x)",
+					ireg->iis>>6, ireg->cmsb);
+			break;
+		case ISAR_IIS_DIAG:
+		case ISAR_IIS_PSTRSP:
+		case ISAR_IIS_PSTEV:
+		case ISAR_IIS_BSTRSP:
+		case ISAR_IIS_IOM2RSP:
+			rcv_mbox(cs, ireg, (u_char *)ireg->par);
+			if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO))
+				== L1_DEB_HSCX) {
+				u_char *tp=debbuf;
+
+				tp += sprintf(debbuf, "msg iis(%x) msb(%x)",
+					ireg->iis, ireg->cmsb);
+				QuickHex(tp, (u_char *)ireg->par, ireg->clsb);
+				debugl1(cs, debbuf);
+			}
+			break;
+		default:
+			rcv_mbox(cs, ireg, debbuf);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+			break;
+	}
+	restore_flags(flags);
+}
+
+void
+setup_pump(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) {
+				if (cs->debug)
+					debugl1(cs, "isar pump bypass cfg dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) {
+		if (cs->debug)
+			debugl1(cs, "isar pump status req dp%d failed",
+				bcs->hw.isar.dpath);
+	}
+}
+
+void
+setup_sart(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) {
+				if (cs->debug)
+					debugl1(cs, "isar sart disable dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+		case L1_MODE_TRANS:
+			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) {
+				if (cs->debug)
+					debugl1(cs, "isar sart binary dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+		case L1_MODE_HDLC:
+			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) {
+				if (cs->debug)
+					debugl1(cs, "isar sart binary dp%d failed",
+						bcs->hw.isar.dpath);
+			}
+			break;
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) {
+		if (cs->debug)
+			debugl1(cs, "isar buf stat req dp%d failed",
+				bcs->hw.isar.dpath);
+	}
+}
+
+void
+setup_iom2(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char cmsb = 0, msg[5] = {0x10,0,0,0,0};
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			/* dummy slot */
+			msg[1] = msg[3] = bcs->hw.isar.dpath + 2;
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			cmsb = 0x80;
+			if (bcs->channel)
+				msg[1] = msg[3] = 1;
+			break;
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) {
+		if (cs->debug)
+			debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath);
+	}
+	if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) {
+		if (cs->debug)
+			debugl1(cs, "isar IOM2 cfg req dp%d failed",
+				bcs->hw.isar.dpath);
+	}
+}
+
+int
+modeisar(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	/* Here we are selecting the best datapath for requested mode */
+	if(bcs->mode == L1_MODE_NULL) { /* New Setup */
+		bcs->channel = bc;
+		switch (mode) {
+			case L1_MODE_NULL: /* init */
+				break;
+			case L1_MODE_TRANS:
+			case L1_MODE_HDLC:
+				/* best is datapath 2 */
+				if (!test_and_set_bit(ISAR_DP2_USE, 
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 2;
+				else if (!test_and_set_bit(ISAR_DP1_USE,
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 1;
+				else {
+					printk(KERN_ERR"isar modeisar both pathes in use\n");
+					return(1);
+				}
+				break;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "isar dp%d mode %d->%d ichan %d",
+			bcs->hw.isar.dpath, bcs->mode, mode, bc);
+	bcs->mode = mode;
+	setup_pump(bcs);
+	setup_sart(bcs);
+	setup_iom2(bcs);
+	if (bcs->mode == L1_MODE_NULL) {
+		/* Clear resources */
+		if (bcs->hw.isar.dpath == 1)
+			test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags);
+		else if (bcs->hw.isar.dpath == 2)
+			test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags);
+		bcs->hw.isar.dpath = 0;
+	}
+	return(0);
+}
+
+void
+isar_setup(struct IsdnCardState *cs)
+{
+	u_char msg;
+	int i;
+	
+	/* Dpath 1, 2 */
+	msg = 61;
+	for (i=0; i<2; i++) {
+		/* Buffer Config */
+		if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+			ISAR_HIS_P12CFG, 4, 1, &msg)) {
+			if (cs->debug)
+				debugl1(cs, "isar P%dCFG failed", i+1);
+		}
+		cs->bcs[i].hw.isar.mml = msg;
+		cs->bcs[i].mode = 0;
+		cs->bcs[i].hw.isar.dpath = i + 1;
+		modeisar(&cs->bcs[i], 0, 0);
+	}
+}
+
+void
+isar_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+				if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+					debugl1(st->l1.bcs->cs, "DRQ set BC_FLG_BUSY");
+				st->l1.bcs->hw.isar.txcnt = 0;
+				restore_flags(flags);
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n");
+				break;
+			}
+			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(st->l1.bcs->cs, "PUI set BC_FLG_BUSY");
+			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->hw.isar.txcnt = 0;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			modeisar(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY");
+			modeisar(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_isarstate(struct BCState *bcs)
+{
+	modeisar(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.isar.rcvbuf) {
+			kfree(bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvbuf = NULL;
+		}
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
+		}
+	}
+}
+
+int
+open_isarstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for isar.rcvbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "openisar clear BC_FLG_BUSY");
+	bcs->event = 0;
+	bcs->hw.isar.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_isar(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_isarstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = isar_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+HISAX_INITFUNC(void 
+initisar(struct IsdnCardState *cs))
+{
+	cs->bcs[0].BC_SetStack = setstack_isar;
+	cs->bcs[1].BC_SetStack = setstack_isar;
+	cs->bcs[0].BC_Close = close_isarstate;
+	cs->bcs[1].BC_Close = close_isarstate;
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isar.h linux/drivers/isdn/hisax/isar.h
--- v2.0.35/linux/drivers/isdn/hisax/isar.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isar.h	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,97 @@
+/* $Id: isar.h,v 1.1.2.3 1998/10/04 23:05:01 keil Exp $
+ * isar.h   ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.h,v $
+ * Revision 1.1.2.3  1998/10/04 23:05:01  keil
+ * ISAR works now
+ *
+ * Revision 1.1.2.2  1998/09/30 22:28:09  keil
+ * more work for isar support
+ *
+ * Revision 1.1.2.1  1998/09/27 13:01:44  keil
+ * Start support for ISAR based cards
+ *
+ * Revision 1.1  1998/08/13 23:33:48  keil
+ * First version, only init
+ *
+ *
+ */
+ 
+#define ISAR_IRQMSK	0x04
+#define ISAR_IRQSTA	0x04
+#define ISAR_IRQBIT	0x75
+#define ISAR_CTRL_H	0x61
+#define ISAR_CTRL_L	0x60
+#define ISAR_IIS	0x58
+#define ISAR_IIA	0x58
+#define ISAR_HIS	0x50
+#define ISAR_HIA	0x50
+#define ISAR_MBOX	0x4c
+#define ISAR_WADR	0x4a
+#define ISAR_RADR	0x48 
+
+#define ISAR_HIS_VNR	0x14
+#define ISAR_HIS_DKEY	0x02
+#define ISAR_HIS_FIRM	0x1e
+#define ISAR_HIS_STDSP  0x08
+#define ISAR_HIS_DIAG	0x05
+#define ISAR_HIS_P0CFG	0x3c
+#define ISAR_HIS_P12CFG	0x24
+#define ISAR_HIS_SARTCFG	0x25	
+#define ISAR_HIS_PUMPCFG	0x26	
+#define ISAR_HIS_IOM2CFG	0x27
+#define ISAR_HIS_IOM2REQ	0x07
+#define ISAR_HIS_BSTREQ	0x0c
+#define ISAR_HIS_PSTREQ	0x0e
+#define ISAR_HIS_SDATA	0x20
+#define ISAR_HIS_DPS1	0x40
+#define ISAR_HIS_DPS2	0x80
+#define SET_DPS(x)	((x<<6) & 0xc0)
+
+#define ISAR_IIS_MSCMSD 0x3f
+#define ISAR_IIS_VNR	0x15
+#define ISAR_IIS_DKEY	0x03
+#define ISAR_IIS_FIRM	0x1f
+#define ISAR_IIS_STDSP  0x09
+#define ISAR_IIS_DIAG	0x25
+#define ISAR_IIS_GSTEV	0x0
+#define ISAR_IIS_BSTEV	0x28
+#define ISAR_IIS_BSTRSP	0x2c
+#define ISAR_IIS_PSTRSP	0x2e
+#define ISAR_IIS_PSTEV	0x2a
+#define ISAR_IIS_IOM2RSP	0x27
+
+#define ISAR_IIS_RDATA	0x20
+#define ISAR_CTRL_SWVER	0x10
+#define ISAR_CTRL_STST	0x40
+
+#define ISAR_MSG_HWVER	{0x20, 0, 1}
+
+#define ISAR_DP1_USE	1
+#define ISAR_DP2_USE	2
+
+#define PMOD_BYPASS	7
+
+#define SMODE_DISABLE	0
+#define SMODE_HDLC	3
+#define SMODE_BINARY	4
+
+#define HDLC_FED	0x40
+#define HDLC_FSD	0x20
+#define HDLC_FST	0x20
+#define HDLC_ERROR	0x1c
+
+#define BSTAT_RDM0	0x1
+#define BSTAT_RDM1	0x2
+#define BSTAT_RDM2	0x4
+#define BSTAT_RDM3	0x8
+
+
+extern int ISARVersion(struct IsdnCardState *cs, char *s);
+extern int isar_load_firmware(struct IsdnCardState *cs, u_char *buf);
+extern void isar_int_main(struct IsdnCardState *cs);
+extern void initisar(struct IsdnCardState *cs);
+extern void isar_fill_fifo(struct BCState *bcs);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- v2.0.35/linux/drivers/isdn/hisax/isdnl1.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/isdnl1.c	Sun Nov 15 10:32:58 1998
@@ -1,9 +1,13 @@
-/* $Id: isdnl1.c,v 1.15 1997/05/27 15:17:55 fritz Exp $
+/* $Id: isdnl1.c,v 1.15.2.19 1998/11/03 00:06:48 keil 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)
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
  *
  * Thanks to    Jan den Ouden
  *              Fritz Elfert
@@ -11,630 +15,379 @@
  *
  *
  * $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.15.2.19  1998/11/03 00:06:48  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.15.2.18  1998/09/30 22:26:35  keil
+ * Add init of l1.Flags
+ *
+ * Revision 1.15.2.17  1998/09/27 23:54:17  keil
+ * cosmetics
+ *
+ * Revision 1.15.2.16  1998/09/27 13:06:22  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.15.2.15  1998/09/12 18:44:00  niemann
+ * Added new card: Sedlbauer ISDN-Controller PC/104
+ *
+ * Revision 1.15.2.14  1998/08/25 14:01:35  calle
+ * Ported driver for AVM Fritz!Card PCI from the 2.1 tree.
+ * I could not test it.
+ *
+ * Revision 1.15.2.13  1998/07/15 14:43:37  calle
+ * Support for AVM passive PCMCIA cards:
+ *    A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 1.15.2.12  1998/05/27 18:05:43  keil
+ * HiSax 3.0
+ *
+ * Revision 1.15.2.11  1998/05/26 10:36:51  keil
+ * fixes from certification
  *
- * Revision 1.14  1997/04/07 23:00:08  keil
- * GFP_KERNEL ---> GFP_ATOMIC
+ * Revision 1.15.2.10  1998/04/11 18:47:45  keil
+ * Fixed bug which was overwriting nrcards
+ * New card support
  *
- * Revision 1.13  1997/04/06 22:55:50  keil
- * Using SKB's
+ * Revision 1.15.2.9  1998/04/08 21:52:00  keil
+ * new debug
  *
- * Revision 1.12  1997/03/26 13:43:57  keil
- * small cosmetics
+ * Revision 1.15.2.8  1998/03/07 23:15:26  tsbogend
+ * made HiSax working on Linux/Alpha
  *
- * Revision 1.11  1997/03/25 23:11:23  keil
- * US NI-1 protocol
+ * Revision 1.15.2.7  1998/02/11 14:23:14  keil
+ * support for Dr Neuhaus Niccy PnP and PCI
  *
- * Revision 1.10  1997/03/13 14:45:05  keil
- * using IRQ proof queue_task
+ * Revision 1.15.2.6  1998/02/09 11:24:11  keil
+ * New leased line support (Read README.HiSax!)
  *
- * Revision 1.9  1997/03/12 21:44:21  keil
- * change Interrupt routine from atomic quick to normal
+ * Revision 1.15.2.5  1998/01/27 22:33:55  keil
+ * dynalink ----> asuscom
  *
- * Revision 1.8  1997/02/09 00:24:31  keil
- * new interface handling, one interface per card
+ * Revision 1.15.2.4  1998/01/11 22:55:20  keil
+ * 16.3c support
  *
- * Revision 1.7  1997/01/27 15:56:03  keil
- * PCMCIA Teles card and ITK ix1 micro added
+ * Revision 1.15.2.3  1997/11/15 18:50:34  keil
+ * new common init function
  *
- * Revision 1.6  1997/01/21 22:20:00  keil
- * changes for D-channel log; Elsa Quickstep support
+ * Revision 1.15.2.2  1997/10/17 22:13:54  keil
+ * update to last hisax version
  *
- * Revision 1.5  1997/01/10 12:51:19  keil
- * cleanup; set newversion
+ * Revision 2.6  1997/09/12 10:05:16  keil
+ * ISDN_CTRL_DEBUG define
  *
- * Revision 1.4  1996/12/08 19:44:53  keil
- * L2FRAME_DEBUG and other changes from Pekka Sarnila
+ * Revision 2.5  1997/09/11 17:24:45  keil
+ * Add new cards
  *
- * Revision 1.3  1996/11/18 15:34:47  keil
- * fix HSCX version code
+ * Revision 2.4  1997/08/15 17:47:09  keil
+ * avoid oops because a uninitialised timer
  *
- * Revision 1.2  1996/10/27 22:16:54  keil
- * ISAC/HSCX version lookup
+ * Revision 2.3  1997/08/01 11:16:40  keil
+ * cosmetics
  *
- * Revision 1.1  1996/10/13 20:04:53  keil
- * Initial revision
+ * Revision 2.2  1997/07/30 17:11:08  keil
+ * L1deactivated exported
  *
+ * Revision 2.1  1997/07/27 21:35:38  keil
+ * new layer1 interface
  *
+ * Revision 2.0  1997/06/26 11:02:53  keil
+ * New Layer and card interface
+ *
+ * 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.
+ *
+ * old changes removed KKe
  *
  */
 
-const char *l1_revision = "$Revision: 1.15 $";
+const char *l1_revision = "$Revision: 1.15.2.19 $";
 
 #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
+#define TIMER3_VALUE 7000
 
-#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;
-	}
+static
+struct Fsm l1fsm_b =
+{NULL, 0, 0, NULL, NULL};
+
+static
+struct Fsm l1fsm_d =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L1_F2,
+	ST_L1_F3,
+	ST_L1_F4,
+	ST_L1_F5,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1D_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1DState[] =
+{
+	"ST_L1_F2",
+	"ST_L1_F3",
+	"ST_L1_F4",
+	"ST_L1_F5",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+enum {
+	ST_L1_NULL,
+	ST_L1_WAIT_ACT,
+	ST_L1_WAIT_DEACT,
+	ST_L1_ACTIV,
+};
+
+#define L1B_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strL1BState[] =
+{
+	"ST_L1_NULL",
+	"ST_L1_WAIT_ACT",
+	"ST_L1_WAIT_DEACT",
+	"ST_L1_ACTIV",
+};
+
+enum {
+	EV_PH_ACTIVATE,
+	EV_PH_DEACTIVATE,
+	EV_RESET_IND,
+	EV_DEACT_CNF,
+	EV_DEACT_IND,
+	EV_POWER_UP,
+	EV_RSYNC_IND, 
+	EV_INFO2_IND,
+	EV_INFO4_IND,
+	EV_TIMER_DEACT,
+	EV_TIMER_ACT,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+	"EV_PH_ACTIVATE",
+	"EV_PH_DEACTIVATE",
+	"EV_RESET_IND",
+	"EV_DEACT_CNF",
+	"EV_DEACT_IND",
+	"EV_POWER_UP",
+	"EV_RSYNC_IND", 
+	"EV_INFO2_IND",
+	"EV_INFO4_IND",
+	"EV_TIMER_DEACT",
+	"EV_TIMER_ACT",
+	"EV_TIMER3",
+};
+
+void
+debugl1(struct IsdnCardState *cs, char *fmt, ...)
+{
+	va_list args;
+	char tmp[8];
+	
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
+}
+
+static void
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	char tmp[8];
+	
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
 }
 
 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)
+L1activated(struct IsdnCardState *cs)
 {
 	struct PStack *st;
 
-	st = sp->stlist;
-	while (st)
-		if (st->l1.act_state)
-			return (!0);
+	st = cs->stlist;
+	while (st) {
+		if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
 		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;
+			st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
+		st = st->next;
 	}
 }
 
-
-static void
-act_ivated(struct IsdnCardState *sp)
+void
+L1deactivated(struct IsdnCardState *cs)
 {
 	struct PStack *st;
 
-	st = sp->stlist;
+	st = cs->stlist;
 	while (st) {
-		if (st->l1.act_state == 1) {
-			st->l1.act_state = 2;
-			st->l1.l1man(st, PH_ACTIVATE, NULL);
-		}
+		if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+		st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
 		st = st->next;
 	}
+	test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
 }
 
-static void
-process_new_ph(struct IsdnCardState *sp)
-{
-	if (sp->ph_active == 5)
-		act_ivated(sp);
-}
-
-static void
-process_xmt(struct IsdnCardState *sp)
+void
+DChannel_proc_xmt(struct IsdnCardState *cs)
 {
 	struct PStack *stptr;
 
-	if (sp->tx_skb)
+	if (cs->tx_skb)
 		return;
 
-	stptr = sp->stlist;
+	stptr = cs->stlist;
 	while (stptr != NULL)
-		if (stptr->l1.requestpull) {
-			stptr->l1.requestpull = 0;
-			stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+		if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
+			stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
 			break;
 		} else
 			stptr = stptr->next;
 }
 
-static void
-process_rcv(struct IsdnCardState *sp)
+void
+DChannel_proc_rcv(struct IsdnCardState *cs)
 {
 	struct sk_buff *skb, *nskb;
-	struct PStack *stptr;
-	int found, broadc;
-	char tmp[64];
+	struct PStack *stptr = cs->stlist;
+	int found, tei, sapi;
 
-	while ((skb = skb_dequeue(&sp->rq))) {
+	if (stptr)
+		if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
+			FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL);	
+	while ((skb = skb_dequeue(&cs->rq))) {
 #ifdef L2FRAME_DEBUG		/* psa */
-		if (sp->debug & L1_DEB_LAPD)
-			Logl2Frame(sp, skb, "PH_DATA", 1);
+		if (cs->debug & L1_DEB_LAPD)
+			Logl2Frame(cs, 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)
+		stptr = cs->stlist;
+		sapi = skb->data[0] >> 2;
+		tei = skb->data[1] >> 1;
+
+		if (cs->debug & DEB_DLOG_HEX)
+			LogFrame(cs, skb->data, skb->len);
+		if (cs->debug & DEB_DLOG_VERBOSE)
+			dlogframe(cs, skb, 1);
+		if (tei == GROUP_TEI) {
+			if (sapi == CTRL_SAPI) { /* sapi 0 */
+				while (stptr != NULL) {
 					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
-						stptr->l1.l1l2(stptr, PH_DATA, nskb);
+						stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
 					else
 						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
-				stptr = stptr->next;
+					stptr = stptr->next;
+				}
+			} else if (sapi == TEI_SAPI) {
+				while (stptr != NULL) {
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb);
+					else
+						printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
+					stptr = stptr->next;
+				}
 			}
-			SET_SKB_FREE(skb);
 			dev_kfree_skb(skb, FREE_READ);
-		} else {
+		} else if (sapi == CTRL_SAPI) { /* sapi 0 */
 			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);
+				if (tei == stptr->l2.tei) {
+					stptr->l1.l1l2(stptr, PH_DATA | INDICATION, 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);
+			if (!found)
 				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)
+BChannel_proc_xmt(struct BCState *bcs)
 {
-	struct PStack *st = hsp->st;
+	struct PStack *st = bcs->st;
 
-	if (hsp->tx_skb)
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		debugl1(bcs->cs, "BC_BUSY Error");
 		return;
+	}
 
-	if (st->l1.requestpull) {
-		st->l1.requestpull = 0;
-		st->l1.l1l2(st, PH_PULL_ACK, NULL);
+	if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
+		st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+	if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
+		if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) {
+			st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, 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)
+BChannel_proc_rcv(struct BCState *bcs)
 {
 	struct sk_buff *skb;
 
-#ifdef DEBUG_MAGIC
-	if (hsp->magic != 301270) {
-		printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
-		return;
+	if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) {
+		FsmDelTimer(&bcs->st->l1.timer, 4);
+		FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
 	}
-#endif
-	while ((skb = skb_dequeue(&hsp->rqueue))) {
-		hsp->st->l1.l1l2(hsp->st, PH_DATA, skb);
+	while ((skb = skb_dequeue(&bcs->rqueue))) {
+		bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
 	}
 }
 
 static void
-hscx_bh(struct HscxState *hsp)
+BChannel_bh(struct BCState *bcs)
 {
-
-	if (!hsp)
+	if (!bcs)
 		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);
-
+	if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event))
+		BChannel_proc_rcv(bcs);
+	if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event))
+		BChannel_proc_xmt(bcs);
 }
 
-/*
- * interrupt stuff ends here
- */
-
 void
-HiSax_addlist(struct IsdnCardState *sp,
+HiSax_addlist(struct IsdnCardState *cs,
 	      struct PStack *st)
 {
-	st->next = sp->stlist;
-	sp->stlist = st;
+	st->next = cs->stlist;
+	cs->stlist = st;
 }
 
 void
-HiSax_rmlist(struct IsdnCardState *sp,
+HiSax_rmlist(struct IsdnCardState *cs,
 	     struct PStack *st)
 {
 	struct PStack *p;
 
-	if (sp->stlist == st)
-		sp->stlist = st->next;
+	FsmDelTimer(&st->l1.timer, 0);
+	if (cs->stlist == st)
+		cs->stlist = st->next;
 	else {
-		p = sp->stlist;
+		p = cs->stlist;
 		while (p)
 			if (p->next == st) {
 				p->next = st->next;
@@ -644,735 +397,477 @@
 	}
 }
 
-static void
-check_ph_act(struct IsdnCardState *sp)
+void
+init_bcstate(struct IsdnCardState *cs,
+	     int bc)
 {
-	struct PStack *st = sp->stlist;
+	struct BCState *bcs = cs->bcs + bc;
 
-	while (st) {
-		if (st->l1.act_state)
-			return;
-		st = st->next;
-	}
-	if (sp->ph_active == 5)
-		sp->ph_active = 4;
+	bcs->cs = cs;
+	bcs->channel = bc;
+	bcs->tqueue.next = 0;
+	bcs->tqueue.sync = 0;
+	bcs->tqueue.routine = (void *) (void *) BChannel_bh;
+	bcs->tqueue.data = bcs;
+	bcs->BC_SetStack = NULL;
+	bcs->BC_Close = NULL;
+	bcs->Flag = 0;
 }
 
-static void
-HiSax_manl1(struct PStack *st, int pr,
-	    void *arg)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	long flags;
-	char tmp[32];
+#ifdef L2FRAME_DEBUG		/* psa */
 
-	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;
+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 void
-HiSax_l2l1discardq(struct PStack *st, int pr,
-		   void *heldby, int releasetoo)
-{
-	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
-	struct sk_buff *skb;
+static char tmpdeb[32];
 
-#ifdef DEBUG_MAGIC
-	if (sp->magic != 301271) {
-		printk(KERN_DEBUG "isac_discardq magic not 301271\n");
-		return;
+char *
+l2frames(u_char * ptr)
+{
+	switch (ptr[2] & ~0x10) {
+		case 1:
+		case 5:
+		case 9:
+			sprintf(tmpdeb, "%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(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+			break;
+		default:
+			if (!(ptr[2] & 1)) {
+				sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+				break;
+			} else
+				return "invalid command";
 	}
-#endif
 
-	while ((skb = skb_dequeue(&sp->sq)))
-		dev_kfree_skb(skb, FREE_WRITE);
+
+	return tmpdeb;
 }
 
 void
-setstack_HiSax(struct PStack *st, struct IsdnCardState *sp)
+Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
 {
-	st->l1.hardware = sp;
-	st->protocol = sp->protocol;
+	u_char *ptr;
 
-	setstack_tei(st);
+	ptr = skb->data;
 
-	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;
+	if (ptr[0] & 1 || !(ptr[1] & 1))
+		debugl1(cs, "Address not LAPD");
+	else
+		debugl1(cs, "%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);
 }
+#endif
 
-void
-init_hscxstate(struct IsdnCardState *sp,
-	       int hscx)
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
 {
-	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;
+	FsmChangeState(fi, ST_L1_F3);
+}
 
-	hsp->inuse = 0;
-	hsp->init = 0;
-	hsp->active = 0;
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
-#ifdef DEBUG_MAGIC
-	hsp->magic = 301270;
-#endif
+	FsmChangeState(fi, ST_L1_F3);
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
 }
 
-int
-get_irq(int cardnr, void *routine)
+static void
+l1_deact_req(struct FsmInst *fi, int event, void *arg)
 {
-	struct IsdnCard *card = cards + cardnr;
-	long flags;
+	struct PStack *st = fi->userdata;
 
-	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);
+	FsmChangeState(fi, ST_L1_F3);
+//	if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) {
+		FsmDelTimer(&st->l1.timer, 1);
+		FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+		test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+//	}
 }
 
 static void
-release_irq(int cardnr)
+l1_power_up(struct FsmInst *fi, int event, void *arg)
 {
-	struct IsdnCard *card = cards + cardnr;
+	struct PStack *st = fi->userdata;
 
-	irq2dev_map[card->sp->irq] = NULL;
-	free_irq(card->sp->irq, NULL);
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
+		FsmChangeState(fi, ST_L1_F4);
+		st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+		FsmDelTimer(&st->l1.timer, 1);
+		FsmAddTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+		test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+	} else
+		FsmChangeState(fi, ST_L1_F3);
 }
 
-void
-close_hscxstate(struct HscxState *hs)
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
 {
-	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;
+	FsmChangeState(fi, ST_L1_F5);
 }
 
 static void
-closecard(int cardnr)
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
 {
-	struct IsdnCardState *csta = cards[cardnr].sp;
-	struct sk_buff *skb;
+	FsmChangeState(fi, ST_L1_F8);
+}
 
-	close_hscxstate(csta->hs + 1);
-	close_hscxstate(csta->hs);
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
-	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);
+	FsmChangeState(fi, ST_L1_F6);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
 }
 
-static int
-checkcard(int cardnr, char *id)
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
 {
-	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;
+	struct PStack *st = fi->userdata;
 
-	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);
+	FsmChangeState(fi, ST_L1_F7);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+	if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
+		FsmDelTimer(&st->l1.timer, 4);
+	if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
+		if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags))
+			FsmDelTimer(&st->l1.timer, 3);
+		FsmAddTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
+		test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
 	}
-	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);
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
-	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;
+	test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);	
+	if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		L1deactivated(st->l1.hardware);
+	if (st->l1.l1m.state != ST_L1_F6) {
+		FsmChangeState(fi, ST_L1_F3);
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
 	}
-	if (!ret) {
-		closecard(cardnr);
-		return (0);
-	}
-	init_tei(sp, sp->protocol);
-	CallcNewChan(sp);
-	ll_run(sp);
-	return (1);
 }
 
-void
-HiSax_shiftcards(int idx)
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
 {
-	int i;
+	struct PStack *st = fi->userdata;
+	
+	test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+	test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1activated(st->l1.hardware);
+}
 
-	for (i = idx; i < 15; i++)
-		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	
+	test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1deactivated(st->l1.hardware);
+	st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL);
 }
 
-int
-HiSax_inithardware(void)
+static void
+l1_activate(struct FsmInst *fi, int event, void *arg)
 {
-	int foundcards = 0;
-	int i = 0;
-	int t = ',';
-	int flg = 0;
-	char *id;
-	char *next_id = HiSax_id;
-	char ids[20];
+	struct PStack *st = fi->userdata;
+                
+	st->l1.l1hw(st, HW_RESET | REQUEST, NULL);
+}
 
-	if (strchr(HiSax_id, ','))
-		t = ',';
-	else if (strchr(HiSax_id, '%'))
-		t = '%';
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
-	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);
-		}
+	if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) {
+		test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+		L1deactivated(st->l1.hardware);
 	}
-	return foundcards;
 }
 
-void
-HiSax_closehardware(void)
+static struct FsmNode L1DFnList[] HISAX_INITDATA =
 {
-	int i;
-	long flags;
+	{ST_L1_F3, EV_PH_ACTIVATE, l1_activate},
+	{ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
+	{ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
+	{ST_L1_F3, EV_RESET_IND, l1_reset},
+	{ST_L1_F4, EV_RESET_IND, l1_reset},
+	{ST_L1_F5, EV_RESET_IND, l1_reset},
+	{ST_L1_F6, EV_RESET_IND, l1_reset},
+	{ST_L1_F7, EV_RESET_IND, l1_reset},
+	{ST_L1_F8, EV_RESET_IND, l1_reset},
+	{ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_IND, l1_deact_req},
+	{ST_L1_F7, EV_DEACT_IND, l1_deact_req},
+	{ST_L1_F8, EV_DEACT_IND, l1_deact_req},
+	{ST_L1_F3, EV_POWER_UP, l1_power_up},
+	{ST_L1_F4, EV_RSYNC_IND, l1_go_F5},
+	{ST_L1_F6, EV_RSYNC_IND, l1_go_F8},
+	{ST_L1_F7, EV_RSYNC_IND, l1_go_F8},
+	{ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F3, EV_TIMER3, l1_timer3},
+	{ST_L1_F4, EV_TIMER3, l1_timer3},
+	{ST_L1_F5, EV_TIMER3, l1_timer3},
+	{ST_L1_F6, EV_TIMER3, l1_timer3},
+	{ST_L1_F8, EV_TIMER3, l1_timer3},
+	{ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
+	{ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
+};
 
-	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);
-}
+#define L1D_FN_COUNT (sizeof(L1DFnList)/sizeof(struct FsmNode))
 
 static void
-hscx_l2l1(struct PStack *st, int pr, void *arg)
+l1b_activate(struct FsmInst *fi, int event, 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;
-	}
+	struct PStack *st = fi->userdata;
 
+	FsmChangeState(fi, ST_L1_WAIT_ACT);
+	FsmAddTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
 }
-extern struct IsdnBuffers *tracebuf;
 
 static void
-hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
-		  int releasetoo)
+l1b_deactivate(struct FsmInst *fi, int event, void *arg)
 {
-	struct IsdnCardState *sp = (struct IsdnCardState *)
-	st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
-	struct sk_buff *skb;
+	struct PStack *st = fi->userdata;
 
-#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);
+	FsmChangeState(fi, ST_L1_WAIT_DEACT);
+	FsmAddTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
 }
 
-static int
-open_hscxstate(struct IsdnCardState *sp,
-	       int hscx)
+static void
+l1b_timer_act(struct FsmInst *fi, int event, void *arg)
 {
-	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;
+	struct PStack *st = fi->userdata;
 
-	hsp->tx_skb = NULL;
-	hsp->event = 0;
-	hsp->rcvidx = 0;
-	hsp->tx_cnt = 0;
-	return (0);
+	FsmChangeState(fi, ST_L1_ACTIV);
+	st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
 }
 
 static void
-hscx_manl1(struct PStack *st, int pr,
-	   void *arg)
+l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
 {
-	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
-	struct HscxState *hsp = sp->hs + st->l1.hscx;
+	struct PStack *st = fi->userdata;
 
-	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;
-	}
+	FsmChangeState(fi, ST_L1_NULL);
+	st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
 }
 
-int
-setstack_hscx(struct PStack *st, struct HscxState *hs)
+static struct FsmNode L1BFnList[] HISAX_INITDATA =
 {
-	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_NULL, EV_PH_ACTIVATE, l1b_activate},
+	{ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act},
+	{ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate},
+	{ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact},
+};
 
-	st->l1.act_state = 0;
-	st->l1.requestpull = 0;
+#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
 
-	hs->st = st;
-	return (0);
+HISAX_INITFUNC(void Isdnl1New(void))
+{
+	l1fsm_d.state_count = L1D_STATE_COUNT;
+	l1fsm_d.event_count = L1_EVENT_COUNT;
+	l1fsm_d.strEvent = strL1Event;
+	l1fsm_d.strState = strL1DState;
+	FsmNew(&l1fsm_d, L1DFnList, L1D_FN_COUNT);
+	l1fsm_b.state_count = L1B_STATE_COUNT;
+	l1fsm_b.event_count = L1_EVENT_COUNT;
+	l1fsm_b.strEvent = strL1Event;
+	l1fsm_b.strState = strL1BState;
+	FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
 }
 
-void
-HiSax_reportcard(int cardnr)
+void Isdnl1Free(void)
 {
-	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);
+	FsmFree(&l1fsm_d);
+	FsmFree(&l1fsm_b);
 }
 
-#ifdef L2FRAME_DEBUG		/* psa */
-
-char *
-l2cmd(u_char cmd)
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
 {
-	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";
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+		case (PH_PULL | REQUEST):
+		case (PH_PULL |INDICATION):
+			st->l1.l1hw(st, pr, arg);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			if (cs->debug)
+				debugl1(cs, "PH_ACTIVATE_REQ %s",
+					strL1DState[st->l1.l1m.state]);
+			if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
+				st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			else {
+				test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+				FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
+			}
+			break;
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
 		default:
-			if (!(cmd & 1))
-				return "I";
-			else
-				return "invalid command";
+			if (cs->debug)
+				debugl1(cs, "dch_l2l1 msg %04X unhandled", pr);
+			break;
 	}
 }
 
-static char tmp[20];
+void
+l1_msg(struct IsdnCardState *cs, int pr, void *arg) {
+	struct PStack *st;
 
-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);
+	st = cs->stlist;
+	
+	while (st) {
+		switch(pr) {
+			case (HW_RESET | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
 				break;
-			} else
-				return "invalid command";
+			case (HW_DEACTIVATE | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+				break;
+			case (HW_DEACTIVATE | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+				break;
+			case (HW_POWERUP | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
+				break;
+			case (HW_RSYNC | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
+				break;
+			case (HW_INFO2 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+				break;
+			case (HW_INFO4_P8 | INDICATION):
+			case (HW_INFO4_P10 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+				break;
+			default:
+				if (cs->debug)
+					debugl1(cs, "l1msg %04X unhandled", pr);
+				break;
+		}
+		st = st->next;
 	}
+}
 
-
-	return tmp;
+void
+l1_msg_b(struct PStack *st, int pr, void *arg) {
+	switch(pr) {
+		case (PH_ACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL);
+			break;
+	}
 }
 
 void
-Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir)
+setstack_HiSax(struct PStack *st, struct IsdnCardState *cs)
 {
-	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);
-	}
+	st->l1.hardware = cs;
+	st->protocol = cs->protocol;
+	st->l1.l1m.fsm = &l1fsm_d;
+	st->l1.l1m.state = ST_L1_F3;
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+	setstack_tei(st);
+	setstack_manager(st);
+	st->l1.stlistp = &(cs->stlist);
+	st->l2.l2l1  = dch_l2l1;
+	st->l1.Flags = 0;
+	cs->setstack_d(st, cs);
 }
 
-#endif
+void
+setstack_l1_B(struct PStack *st)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	st->l1.l1m.fsm = &l1fsm_b;
+	st->l1.l1m.state = ST_L1_NULL;
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	st->l1.Flags = 0;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h
--- v2.0.35/linux/drivers/isdn/hisax/isdnl1.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/isdnl1.h	Sun Nov 15 10:32:58 1998
@@ -1,51 +1,58 @@
-/* $Id: isdnl1.h,v 1.4 1997/04/06 22:55:52 keil Exp $
- *
+/* $Id: isdnl1.h,v 1.4.2.7 1998/11/03 00:06:55 keil Exp $
+
  * $Log: isdnl1.h,v $
- * Revision 1.4  1997/04/06 22:55:52  keil
- * Using SKB's
+ * Revision 1.4.2.7  1998/11/03 00:06:55  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.4.2.6  1998/09/30 22:20:04  keil
+ * Cosmetics
+ *
+ * Revision 1.4.2.5  1998/09/27 13:06:28  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.4.2.4  1998/05/27 18:05:49  keil
+ * HiSax 3.0
+ *
+ * Revision 1.4.2.3  1997/12/01 09:09:08  keil
+ * more l1 debug
+ *
+ * Revision 1.4.2.2  1997/11/15 18:50:40  keil
+ * new common init function
  *
- * Revision 1.3  1996/12/08 19:41:55  keil
- * L2FRAME_DEBUG
+ * Revision 1.4.2.1  1997/10/17 22:13:58  keil
+ * update to last hisax version
  *
- * Revision 1.2  1996/10/27 22:26:27  keil
- * ISAC/HSCX version functions
+ * Revision 2.2  1997/07/30 17:11:09  keil
+ * L1deactivated exported
  *
- * Revision 1.1  1996/10/13 20:03:47  keil
- * Initial revision
+ * Revision 2.1  1997/07/27 21:43:58  keil
+ * new l1 interface
  *
+ * Revision 2.0  1997/06/26 11:02:55  keil
+ * New Layer and card interface
  *
  *
  */
 
-
-#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);
+#define D_RCVBUFREADY	0
+#define D_XMTBUFREADY	1
+#define D_L1STATECHANGE	2
+#define D_CLEARBUSY	3
+#define D_RX_MON0	4
+#define D_RX_MON1	5
+#define D_TX_MON0	6
+#define D_TX_MON1	7
+
+#define B_RCVBUFREADY 0
+#define B_XMTBUFREADY 1
+
+extern void debugl1(struct IsdnCardState *cs, char *fmt, ...);
+extern void DChannel_proc_xmt(struct IsdnCardState *cs);
+extern void DChannel_proc_rcv(struct IsdnCardState *cs);
+extern void l1_msg(struct IsdnCardState *cs, int pr, void *arg);
+extern void l1_msg_b(struct PStack *st, int pr, void *arg);
 
 #ifdef L2FRAME_DEBUG
-extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir);
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c
--- v2.0.35/linux/drivers/isdn/hisax/isdnl2.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/isdnl2.c	Sun Nov 15 10:32:58 1998
@@ -1,62 +1,75 @@
-/* $Id: isdnl2.c,v 1.10 1997/05/06 09:38:13 keil Exp $
+/* $Id: isdnl2.c,v 1.10.2.11 1998/11/03 00:06:57 keil Exp $
 
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * 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.10.2.11  1998/11/03 00:06:57  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.10.2.10  1998/09/27 13:06:30  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.10.2.9  1998/06/19 15:17:56  keil
+ * fix LAPB tx_cnt for none I-frames
+ *
+ * Revision 1.10.2.8  1998/06/18 23:12:05  keil
+ * LAPB bugfix
  *
- * Revision 1.9  1997/04/07 23:02:11  keil
- * missing braces
+ * Revision 1.10.2.7  1998/05/27 18:05:51  keil
+ * HiSax 3.0
  *
- * Revision 1.8  1997/04/06 22:59:59  keil
- * Using SKB's; changing function names; some minor changes
+ * Revision 1.10.2.6  1998/05/26 10:36:57  keil
+ * fixes from certification
  *
- * Revision 1.7  1997/02/09 00:25:44  keil
- * new interface handling, one interface per card
+ * Revision 1.10.2.5  1998/03/07 23:15:31  tsbogend
+ * made HiSax working on Linux/Alpha
  *
- * Revision 1.6  1997/01/21 22:23:42  keil
- * D-channel log changed
+ * Revision 1.10.2.4  1998/01/27 22:44:38  keil
+ * fixed window size calculation
  *
- * Revision 1.5  1997/01/04 13:47:06  keil
- * handling of MDL_REMOVE added (Thanks to Sim Yskes)
+ * Revision 1.10.2.3  1997/11/15 18:54:03  keil
+ * cosmetics
  *
- * Revision 1.4  1996/12/08 19:51:51  keil
- * many fixes from Pekka Sarnila
+ * Revision 1.10.2.2  1997/10/17 22:13:59  keil
+ * update to last hisax version
  *
- * Revision 1.3  1996/11/05 19:39:12  keil
- * X.75 bugfixes Thank to Martin Maurer
+ * Revision 2.2  1997/07/31 11:49:05  keil
+ * Eroor handling for no TEI assign
  *
- * Revision 1.2  1996/10/30 10:20:58  keil
- * X.75 answer of SABMX fixed to response address (AVM X.75 problem)
+ * Revision 2.1  1997/07/27 21:34:38  keil
+ * cosmetics
  *
- * Revision 1.1  1996/10/13 20:04:54  keil
- * Initial revision
+ * Revision 2.0  1997/06/26 11:07:29  keil
+ * New q.921 and X.75 Layer2
  *
  *
+ *  Old log removed KKe
  *
  */
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isdnl2.h"
 
-const char *l2_revision = "$Revision: 1.10 $";
+const char *l2_revision = "$Revision: 1.10.2.11 $";
 
-static void l2m_debug(struct FsmInst *fi, char *s);
+static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
 
+static
 struct Fsm l2fsm =
 {NULL, 0, 0, NULL, NULL};
 
 enum {
 	ST_L2_1,
+	ST_L2_2,
 	ST_L2_3,
 	ST_L2_4,
 	ST_L2_5,
@@ -70,6 +83,7 @@
 static char *strL2State[] =
 {
 	"ST_L2_1",
+	"ST_L2_2",
 	"ST_L2_3",
 	"ST_L2_4",
 	"ST_L2_5",
@@ -81,53 +95,51 @@
 enum {
 	EV_L2_UI,
 	EV_L2_SABMX,
-	EV_L2_UA,
 	EV_L2_DISC,
-	EV_L2_I,
-	EV_L2_RR,
-	EV_L2_REJ,
+	EV_L2_DM,
+	EV_L2_UA,
 	EV_L2_FRMR,
+	EV_L2_SUPER,
+	EV_L2_I,
 	EV_L2_DL_DATA,
+	EV_L2_ACK_PULL,
+	EV_L2_DL_UNIT_DATA,
 	EV_L2_DL_ESTABLISH,
+	EV_L2_DL_RELEASE,
 	EV_L2_MDL_ASSIGN,
 	EV_L2_MDL_REMOVE,
-	EV_L2_DL_UNIT_DATA,
-	EV_L2_DL_RELEASE,
-	EV_L2_MDL_NOTEIPROC,
+	EV_L2_MDL_ERROR,
+	EV_L1_DEACTIVATE,
 	EV_L2_T200,
-	EV_L2_ACK_PULL,
 	EV_L2_T203,
-	EV_L2_RNR,
 };
 
-#define L2_EVENT_COUNT (EV_L2_RNR+1)
+#define L2_EVENT_COUNT (EV_L2_T203+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_DM",
+	"EV_L2_UA",
 	"EV_L2_FRMR",
+	"EV_L2_SUPER",
+	"EV_L2_I",
 	"EV_L2_DL_DATA",
+	"EV_L2_ACK_PULL",
+	"EV_L2_DL_UNIT_DATA",
 	"EV_L2_DL_ESTABLISH",
+	"EV_L2_DL_RELEASE",
 	"EV_L2_MDL_ASSIGN",
 	"EV_L2_MDL_REMOVE",
-	"EV_L2_DL_UNIT_DATA",
-	"EV_L2_DL_RELEASE",
-	"EV_L2_MDL_NOTEIPROC",
+	"EV_L2_MDL_ERROR",
+	"EV_L1_DEACTIVATE",
 	"EV_L2_T200",
-	"EV_L2_ACK_PULL",
 	"EV_L2_T203",
-	"EV_L2_RNR",
 };
 
-int errcount = 0;
-
-static int l2addrsize(struct Layer2 *tsp);
+static int l2addrsize(struct Layer2 *l2);
 
 static void
 InitWin(struct Layer2 *l2)
@@ -143,7 +155,6 @@
 {
 	int i, cnt = 0;
 
-
 	for (i = 0; i < MAX_WINDOW; i++) {
 		if (l2->windowar[i]) {
 			cnt++;
@@ -155,55 +166,51 @@
 		printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt);
 }
 
-static int
+inline int
 cansend(struct PStack *st)
 {
 	int p1;
 
-	p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
-	return (st->l2.vs != p1);
+	p1 = st->l2.vs - st->l2.va;
+	if (p1 < 0)
+		p1 += (test_bit(FLG_MOD128, &st->l2.flag) ? 128 : 8);
+	return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag));
 }
 
-static void
-discard_i_queue(struct PStack *st)
+inline void
+clear_exception(struct Layer2 *l2)
 {
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&st->l2.i_queue))) {
-		SET_SKB_FREE(skb);
-		dev_kfree_skb(skb, FREE_READ);
-	}
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	test_and_clear_bit(FLG_REJEXC, &l2->flag);
+	test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
+	test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
 }
 
-int
-l2headersize(struct Layer2 *tsp, int ui)
+inline int
+l2headersize(struct Layer2 *l2, int ui)
 {
-	return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
+	return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+		(test_bit(FLG_LAPD, &l2->flag) ? 2 : 1));
 }
 
-int
-l2addrsize(struct Layer2 *tsp)
+inline int
+l2addrsize(struct Layer2 *l2)
 {
-	return (tsp->laptype == LAPD ? 2 : 1);
+	return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
 }
 
 static int
-sethdraddr(struct Layer2 *tsp,
-	   u_char * header, int rsp)
+sethdraddr(struct Layer2 *l2, u_char * header, int rsp)
 {
 	u_char *ptr = header;
-	int crbit;
+	int crbit = rsp;
 
-	if (tsp->laptype == LAPD) {
-		crbit = rsp;
-		if (!tsp->orig)
-			crbit = !crbit;
-		*ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
-		*ptr++ = (tsp->tei << 1) | 1;
+	if (test_bit(FLG_LAPD, &l2->flag)) {
+		*ptr++ = (l2->sap << 2) | (rsp ? 2 : 0);
+		*ptr++ = (l2->tei << 1) | 1;
 		return (2);
 	} else {
-		crbit = rsp;
-		if (tsp->orig)
+		if (test_bit(FLG_ORIG, &l2->flag))
 			crbit = !crbit;
 		if (crbit)
 			*ptr++ = 1;
@@ -213,82 +220,118 @@
 	}
 }
 
-static void
-enqueue_ui(struct PStack *st,
-	   struct sk_buff *skb)
+inline static void
+enqueue_super(struct PStack *st,
+	      struct sk_buff *skb)
 {
-	st->l2.l2l1(st, PH_DATA, skb);
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len;
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
 }
 
-static void
-enqueue_super(struct PStack *st,
-	      struct sk_buff *skb)
+#define enqueue_ui(a, b) enqueue_super(a, b)
+
+inline int
+IsUI(u_char * data, int ext)
 {
-	st->l2.l2l1(st, PH_DATA, skb);
+	return ((data[0] & 0xef) == UI);
 }
 
-static int
-legalnr(struct PStack *st, int nr)
+inline int
+IsUA(u_char * data, int ext)
 {
-	struct Layer2 *l2 = &st->l2;
-	int lnr, lvs;
+	return ((data[0] & 0xef) == UA);
+}
 
-	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);
+inline int
+IsDM(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == DM);
 }
 
-static void
-setva(struct PStack *st, int nr)
+inline int
+IsDISC(u_char * data, int ext)
 {
-	struct Layer2 *l2 = &st->l2;
+	return ((data[0] & 0xef) == DISC);
+}
 
-	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);
-		}
-	}
+inline int
+IsRR(u_char * data, int ext)
+{
+	if (ext)
+		return (data[0] == RR);
+	else
+		return ((data[0] & 0xf) == 1);
 }
 
-static void
-l2s1(struct FsmInst *fi, int event, void *arg)
+inline int
+IsSFrame(u_char * data, int ext)
 {
-	struct PStack *st = fi->userdata;
+	register u_char d = *data;
+	
+	if (!ext)
+		d &= 0xf;
+	return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c));
+}
 
-	st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces);
-	FsmChangeState(fi, ST_L2_3);
+inline int
+IsSABMX(u_char * data, int ext)
+{
+	u_char d = data[0] & ~0x10;
+
+	return (ext ? d == SABME : d == SABM);
 }
 
-static void
-l2_send_ui(struct FsmInst *fi, int event, void *arg)
+inline int
+IsREJ(u_char * data, int ext)
 {
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
-	u_char header[MAX_HEADER_LEN];
-	int i;
+	return (ext ? data[0] == REJ : (data[0] & 0xf) == REJ);
+}
 
-	i = sethdraddr(&(st->l2), header, CMD);
-	header[i++] = UI;
-	memcpy(skb_push(skb, i), header, i);
-	enqueue_ui(st, skb);
+inline int
+IsFRMR(u_char * data, int ext)
+{
+	return ((data[0] & 0xef) == FRMR);
+}
+
+inline int
+IsRNR(u_char * data, int ext)
+{
+	return (ext ? data[0] == RNR : (data[0] & 0xf) == RNR);
+}
+
+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 + (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8));
+	lnr = (nr >= l2->va) ? nr : (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+	return (lnr <= lvs);
 }
 
 static void
-l2_receive_ui(struct FsmInst *fi, int event, void *arg)
+setva(struct PStack *st, int nr)
 {
-	struct PStack *st = fi->userdata;
-	struct sk_buff *skb = arg;
+	struct Layer2 *l2 = &st->l2;
+	int len;
 
-	skb_pull(skb, l2headersize(&st->l2, 1));
-	st->l2.l2l3(st, DL_UNIT_DATA, skb);
+	while (l2->va != nr) {
+		l2->va = (l2->va + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+		len = l2->windowar[l2->sow]->len;
+		if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type)
+			len = -1;
+		dev_kfree_skb(l2->windowar[l2->sow], FREE_WRITE);
+		l2->windowar[l2->sow] = NULL;
+		l2->sow = (l2->sow + 1) % l2->window;
+		if (st->lli.l2writewakeup && (len >=0))
+			st->lli.l2writewakeup(st, len);
+	}
 }
 
-inline void
+static void
 send_uframe(struct PStack *st, u_char cmd, u_char cr)
 {
 	struct sk_buff *skb;
@@ -306,53 +349,163 @@
 	enqueue_super(st, skb);
 }
 
+inline u_char
+get_PollFlag(struct PStack * st, struct sk_buff * skb)
+{
+	return (skb->data[l2addrsize(&(st->l2))] & 0x10);
+}
+
+inline void
+FreeSkb(struct sk_buff *skb)
+{
+	dev_kfree_skb(skb, FREE_READ);
+}
+
+
+inline u_char
+get_PollFlagFree(struct PStack *st, struct sk_buff *skb)
+{
+	u_char PF;
+
+	PF = get_PollFlag(st, skb);
+	FreeSkb(skb);
+	return (PF);
+}
+
 static void
 establishlink(struct FsmInst *fi)
 {
 	struct PStack *st = fi->userdata;
 	u_char cmd;
 
-	FsmChangeState(fi, ST_L2_5);
+	clear_exception(&st->l2);
 	st->l2.rc = 0;
+	cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10;
+	send_uframe(st, cmd, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 1);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+	FsmChangeState(fi, ST_L2_5);
+}
+
+static void
+l2_mdl_error(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
 
-	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");
+	switch (event) {
+		case EV_L2_UA:
+			if (get_PollFlagFree(st, skb))
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C');
+			else
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D');
+			break;
+		case EV_L2_DM:
+			if (get_PollFlagFree(st, skb))
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+			else {
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+				establishlink(fi);
+				test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+			}
+			break;
+	}
+}
 
+static void
+l2_dl_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	int state = fi->state;
 
-	cmd = (st->l2.extended ? SABME : SABM) | 0x10; 
-	send_uframe(st, cmd, CMD);
+	
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		FsmChangeState(fi, ST_L2_4);
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	} else {
+		FsmChangeState(fi, ST_L2_3);
+		if (state == ST_L2_1)
+			st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+	}
 }
 
 static void
-l2_establish(struct FsmInst *fi, int event, void *arg)
+l2_send_ui(struct PStack *st)
 {
-	establishlink(fi);
+	struct sk_buff *skb;
+	u_char header[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&(st->l2), header, CMD);
+	header[i++] = UI;
+	while ((skb = skb_dequeue(&st->l2.ui_queue))) {
+		memcpy(skb_push(skb, i), header, i);
+		enqueue_ui(st, skb);
+	}
 }
 
 static void
-l2_send_disconn(struct FsmInst *fi, int event, void *arg)
+l2_put_ui(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct Channel *chanp = st->l4.userdata;
-
-	FsmChangeState(fi, ST_L2_6);
+	struct sk_buff *skb = arg;
 
-	FsmDelTimer(&st->l2.t203_timer, 1);
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 2);
-		st->l2.t200_running = 0;
+	skb_queue_tail(&st->l2.ui_queue, skb);
+	if (fi->state == ST_L2_1) {
+		FsmChangeState(fi, ST_L2_2);
+		st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
 	}
-	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 (fi->state > ST_L2_3)
+		l2_send_ui(st);
+}
+
+static void
+l2_got_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));
+	if (skb->len > st->l2.maxlen) { 
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
+		FreeSkb(skb);
+	} else
+		st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
+}
 
-	if (!((chanp->impair == 2) && (st->l2.laptype == LAPB)))
-		send_uframe(st, DISC | 0x10, CMD);
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (fi->state != ST_L2_4)
+		discard_queue(&st->l2.i_queue);
+	if (fi->state != ST_L2_5)
+		establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_dl_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
 
-	discard_i_queue(st);
+	if (fi->state == ST_L2_4) {
+		st->l2.l2l3(st, DL_RELEASE  | CONFIRM, NULL);
+		return;
+	} else if (fi->state == ST_L2_5) {
+		test_and_set_bit(FLG_PEND_REL, &st->l2.flag);
+		return;
+	}
+	discard_queue(&st->l2.i_queue);
+	FsmChangeState(fi, ST_L2_6);
+	st->l2.rc = 0;
+	send_uframe(st, DISC | 0x10, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 2);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 }
 
 static void
@@ -360,46 +513,59 @@
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	int est = 1, state;
+	int est = 1, state, rsp;
 	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)
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+	if (rsp) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}
+	PollFlag = get_PollFlagFree(st, skb);
+	if (ST_L2_6 == state) {
+		send_uframe(st, DM | PollFlag, RSP);
+		return;
+	} else
+		send_uframe(st, UA | PollFlag, RSP);
+	if (ST_L2_5 == state)
+		return;
+	if (ST_L2_4 != state) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F');
 		if (st->l2.vs != st->l2.va) {
-			discard_i_queue(st);
+			discard_queue(&st->l2.i_queue);
 			est = 1;
 		} else
 			est = 0;
-
+	}
+	clear_exception(&st->l2);
 	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");
+	FsmChangeState(fi, ST_L2_7);
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 2);
+	FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
 
 	if (est)
-		st->l2.l2man(st, DL_ESTABLISH, NULL);
+		st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
 
 	if (ST_L2_8 == state)
 		if (skb_queue_len(&st->l2.i_queue) && cansend(st))
-			st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 }
 
 static void
@@ -407,87 +573,184 @@
 {
 	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);
+	u_char PollFlag, cmd = UA;
+	int state, rel = 1, cst = 1, rsp;
 
-	FsmChangeState(fi, ST_L2_4);
+	state = fi->state;
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
 
-	FsmDelTimer(&st->l2.t203_timer, 3);
-	if (st->l2.t200_running) {
-		FsmDelTimer(&st->l2.t200_timer, 4);
-		st->l2.t200_running = 0;
+	if (rsp) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}
+	PollFlag = get_PollFlagFree(st, skb);
+	if ((state == ST_L2_4) || (state == ST_L2_5)) {
+		rel = 0;
+		cst = 0;
+		cmd = DM;
+	} else if (state == ST_L2_6) {
+		rel = 0;
+		cst = 0;
+	}
+	if (cst) {
+		FsmChangeState(fi, ST_L2_4);
+		FsmDelTimer(&st->l2.t203, 3);
+		if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag)) 
+			FsmDelTimer(&st->l2.t200, 2);
+	}
+	send_uframe(st, cmd | PollFlag, RSP);
+	if (rel) {
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
 	}
-	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)
+l2_got_ua(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	struct Channel *chanp = st->l4.userdata;
+	int pr=-1;
 	u_char PollFlag;
+	int state,rsp;
 
-	skb_pull(skb, l2addrsize(&(st->l2)));
-	PollFlag = *skb->data & 0x10;
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb, FREE_READ);
+	state = fi->state;
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
 
-	if (!((chanp->impair == 1) && (st->l2.laptype == LAPB)))
-		send_uframe(st, DM | (PollFlag ? 0x10 : 0x0), RSP);
+	if (!rsp) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+		FreeSkb(skb);
+		if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}
+	PollFlag = get_PollFlag(st, skb);
+	if (!PollFlag) {
+		l2_mdl_error(fi, event, arg);
+		return;
+	}
+	FreeSkb(skb);
 
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 2);
+	if (fi->state == ST_L2_5) {
+		if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) {
+			discard_queue(&st->l2.i_queue);
+			st->l2.rc = 0;
+			send_uframe(st, DISC | 0x10, CMD);
+			FsmChangeState(fi, ST_L2_6);
+			FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4);
+			test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+		} else {
+			if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
+				pr = DL_ESTABLISH | CONFIRM;
+			} else if (st->l2.vs != st->l2.va) {
+				discard_queue(&st->l2.i_queue);
+				pr = DL_ESTABLISH | INDICATION;
+			}
+			st->l2.vs = 0;
+			st->l2.va = 0;
+			st->l2.vr = 0;
+			st->l2.sow = 0;
+			FsmChangeState(fi, ST_L2_7);
+			if (pr > -1)
+				st->l2.l2l3(st, pr, NULL);
+		}
+	} else {		/* ST_L2_6 */
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+		FsmChangeState(fi, ST_L2_4);
+	}
 }
 
 static void
-l2_got_ua_establish(struct FsmInst *fi, int event, void *arg)
+l2_got_dm(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);
+	u_char PollFlag;
+	int state,rsp;
 
-		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");
+	state = fi->state;
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
 
-		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);
+	if (!rsp) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
+		FreeSkb(skb);
+		if ((state == ST_L2_7) || (state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}	
+	if (skb->len != (l2addrsize(&st->l2) + 1)) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+		FreeSkb(skb);
+		if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
+			establishlink(fi);
+		return;
+	}
+	PollFlag = get_PollFlagFree(st, skb);
+	if (!PollFlag) {
+		if (fi->state == ST_L2_4) {
+			establishlink(fi);
+			test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+			FsmChangeState(fi, ST_L2_5);
+		} else if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) {
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+			establishlink(fi);
+		}
+	} else {
+		switch (fi->state) {
+			case ST_L2_8:
+				 establishlink(fi);
+			case ST_L2_7:
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+				break;
+			case ST_L2_4:
+				break;
+			case ST_L2_5:
+				if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+					FsmDelTimer(&st->l2.t200, 2);
+				discard_queue(&st->l2.i_queue);
+				if (test_bit(FLG_LAPB, &st->l2.flag))
+					st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+				st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+				FsmChangeState(fi, ST_L2_4);
+				break;
+			case ST_L2_6:
+				if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+					FsmDelTimer(&st->l2.t200, 2);
+				if (test_bit(FLG_LAPB, &st->l2.flag))
+					st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+				st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+				FsmChangeState(fi, ST_L2_4);
+				break;
+		}
 	}
 }
 
@@ -501,7 +764,7 @@
 
 	l2 = &st->l2;
 	i = sethdraddr(l2, tmp, cr);
-	if (l2->extended) {
+	if (test_bit(FLG_MOD128, &l2->flag)) {
 		tmp[i++] = typ;
 		tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0);
 	} else
@@ -516,83 +779,143 @@
 }
 
 inline void
-enquiry_response(struct PStack *st, u_char typ, u_char final)
+enquiry_response(struct PStack *st)
 {
-	enquiry_cr(st, typ, RSP, final);
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, RSP, 1);
+	else
+		enquiry_cr(st, RR, RSP, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
 }
 
 inline void
-enquiry_command(struct PStack *st, u_char typ, u_char poll)
+transmit_enquiry(struct PStack *st)
 {
-	enquiry_cr(st, typ, CMD, poll);
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, CMD, 1);
+	else
+		enquiry_cr(st, RR, CMD, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 12);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 }
 
+
 static void
 nrerrorrecovery(struct FsmInst *fi)
 {
-	/* should log error here */
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J');
 	establishlink(fi);
 }
 
 static void
-l2_got_st7_RR(struct FsmInst *fi, int event, void *arg)
+invoke_retransmission(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int p1;
+	long flags;
+
+	if (l2->vs != nr) {
+		save_flags(flags);
+		cli();
+		while (l2->vs != nr) {
+			l2->vs = l2->vs - 1;
+			if (l2->vs < 0)
+				l2->vs += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+			p1 = l2->vs - l2->va;
+			if (p1 < 0)
+				p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+			p1 = (p1 + l2->sow) % l2->window;
+			if (test_bit(FLG_LAPB, &l2->flag))
+				st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0);
+			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+			l2->windowar[p1] = NULL;
+		}
+		restore_flags(flags);
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+	}
+}
+
+static void
+l2_got_st7_super(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;
+	int PollFlag, nr, rsp, typ = RR;
+	struct Layer2 *l2 = &st->l2;
 
-	l2 = &st->l2;
-	if (l2->laptype == LAPD)
-		rsp = *skb->data & 0x2;
-	else
-		rsp = *skb->data == 0x3;
-	if (l2->orig)
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
 		rsp = !rsp;
 
 	skb_pull(skb, l2addrsize(l2));
-	if (l2->extended) {
-		PollFlag = (skb->data[1] & 0x1) == 0x1;
-		seq = skb->data[1] >> 1;
+	if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) {
+		test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+		typ = RNR;
+	} else
+		test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+	if (IsREJ(skb->data, test_bit(FLG_MOD128, &l2->flag)))
+		typ = REJ;
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		if (skb->len == 2) {
+			PollFlag = (skb->data[1] & 0x1) == 0x1;
+			nr = skb->data[1] >> 1;
+		} else {
+			if (skb->len >2) {
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+				establishlink(fi);
+			}
+			FreeSkb(skb);
+			return;
+		}
 	} else {
-		PollFlag = (skb->data[0] & 0x10);
-		seq = (skb->data[0] >> 5) & 0x7;
+		if (skb->len == 1) {
+			PollFlag = (skb->data[0] & 0x10);
+			nr = (skb->data[0] >> 5) & 0x7;
+		} else {
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
 	}
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb, FREE_READ);
+	FreeSkb(skb);
 
-	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);
+	if ((!rsp) && PollFlag)
+		enquiry_response(st);
+	if (rsp && PollFlag)
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A');
+	if (legalnr(st, nr)) {
+		if (typ == REJ) {
+			setva(st, nr);
+			invoke_retransmission(st, nr);
+			if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+				FsmDelTimer(&st->l2.t200, 9);
+			if (FsmAddTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 6))
+				l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ");
+		} else if ((nr == l2->vs) && (typ == RR)) {
+			setva(st, nr);
+			if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+				FsmDelTimer(&st->l2.t200, 9);
+			FsmRestartTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 7);
+		} else if ((l2->va != nr) || (typ == RNR)) {
+			setva(st, nr);
+			FsmDelTimer(&st->l2.t203, 9);
+			FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 6);
+			test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
 		}
+		if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
+			st->l2.l2l1(st, PH_PULL | REQUEST, 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);
+		st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL);
 	}
 }
 
@@ -602,251 +925,216 @@
 	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);
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag)))
+		skb_queue_tail(&st->l2.i_queue, skb);
+	if (fi->state == ST_L2_7)
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 }
 
-static int
-icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr)
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
 {
 	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];
+	int PollFlag, ns, nr, i, rsp;
 
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	if (rsp) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
+		FreeSkb(skb);
+		establishlink(fi);
+		return;
+	}	
 	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;
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		if (skb->len <= (i + 1)) {
+			FreeSkb(skb);
+			return;
+		} else if ((skb->len - i - 1) > l2->maxlen) { 
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
+		PollFlag = ((skb->data[i + 1] & 0x1) == 0x1);
+		ns = 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);
+		if (skb->len <= i) {
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		} else if ((skb->len - i) > l2->maxlen) { 
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
+		PollFlag = (skb->data[i] & 0x10);
+		ns = (skb->data[i] >> 1) & 0x7;
+		nr = (skb->data[i] >> 5) & 0x7;
+	}
+	if (test_bit(FLG_OWN_BUSY, &l2->flag)) {
+		FreeSkb(skb);
+		enquiry_response(st);
+	} else if (l2->vr == ns) {
+		l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
+		test_and_clear_bit(FLG_REJEXC, &l2->flag);
+		if (PollFlag)
+			enquiry_response(st);
+		else
+			test_and_set_bit(FLG_ACK_PEND, &l2->flag);
 		skb_pull(skb, l2headersize(l2, 0));
+		st->l2.l2l3(st, DL_DATA | INDICATION, skb);
 	} 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);
+		FreeSkb(skb);
+		if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+			if (PollFlag)
+				enquiry_response(st);
 		} else {
-			st->l2.rejexp = !0;
-			enquiry_command(st, REJ, 1);
+			enquiry_cr(st, REJ, RSP, PollFlag);
+			test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
 		}
 	}
-	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);
+		setva(st, nr);
+		if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) {
+			if (nr == st->l2.vs) {
+				if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+					FsmDelTimer(&st->l2.t200, 10);
+				FsmRestartTimer(&st->l2.t203, st->l2.T203,
+						EV_L2_T203, NULL, 7);
+			} else if (nr != st->l2.va) {
+				FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
+						NULL, 8);
+				test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+			}
 		}
-	} else
+	} else {
 		nrerrorrecovery(fi);
+		return;
+	}
 
-	if (wasok)
-		st->l2.l2l3(st, DL_DATA, skb);
+	if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+	if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
+		enquiry_cr(st, RR, RSP, 0);
 }
 
 static void
-l2_got_st8_data(struct FsmInst *fi, int event, void *arg)
+l2_got_tei(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);
+	st->l2.tei = (long) arg;
 
-	if (legalnr(st, nr)) {
-		setva(st, nr);
-		if (skb_queue_len(&st->l2.i_queue))
-			st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	if (fi->state == ST_L2_3) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
 	} else
-		nrerrorrecovery(fi);
-
-	if (wasok)
-		st->l2.l2l3(st, DL_DATA, skb);
+		FsmChangeState(fi, ST_L2_4);
+	if (skb_queue_len(&st->l2.ui_queue))
+		l2_send_ui(st);
 }
 
 static void
-l2_got_tei(struct FsmInst *fi, int event, void *arg)
+l2_st5_tout_200(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);
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
+		FsmChangeState(fi, ST_L2_4);
+		test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+		discard_queue(&st->l2.i_queue);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	} else {
+		st->l2.rc++;
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM)
+			    | 0x10, CMD);
 	}
 }
 
 static void
-l2_got_st7_rej(struct FsmInst *fi, int event, void *arg)
+l2_st6_tout_200(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;
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
+		FsmChangeState(fi, ST_L2_4);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H');
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
 	} else {
-		PollFlag = (skb->data[0] & 0x10);
-		seq = (skb->data[0] >> 5) & 0x7;
+		st->l2.rc++;
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
+			    NULL, 9);
+		send_uframe(st, DISC | 0x10, CMD);
 	}
-	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)
+l2_st78_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);
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+	if (fi->state == ST_L2_7) {
+		st->l2.rc = 0;
+		FsmChangeState(fi, ST_L2_8);
+	}
+	if (st->l2.rc == st->l2.N200) {
+		establishlink(fi);
 	} else {
+		transmit_enquiry(st);
 		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)
+l2_st7_tout_203(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);
 
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9);
+		return;
 	}
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+	st->l2.rc = 0;
 }
 
 static void
 l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
-	struct sk_buff *skb;
+	struct sk_buff *skb, *oskb;
 	struct Layer2 *l2 = &st->l2;
 	u_char header[MAX_HEADER_LEN];
 	int p1, i;
@@ -860,7 +1148,7 @@
 
 	p1 = l2->vs - l2->va;
 	if (p1 < 0)
-		p1 += l2->extended ? 128 : 8;
+		p1 += test_bit(FLG_MOD128, &l2->flag) ? 128 : 8;
 	p1 = (p1 + l2->sow) % l2->window;
 	if (l2->windowar[p1]) {
 		printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n",
@@ -871,7 +1159,7 @@
 
 	i = sethdraddr(&st->l2, header, CMD);
 
-	if (l2->extended) {
+	if (test_bit(FLG_MOD128, &l2->flag)) {
 		header[i++] = l2->vs << 1;
 		header[i++] = l2->vr << 1;
 		l2->vs = (l2->vs + 1) % 128;
@@ -879,146 +1167,129 @@
 		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;
+	p1 = skb->data - skb->head;
+	if (p1 >= i)
+		memcpy(skb_push(skb, i), header, i);
+	else {
+		printk(KERN_WARNING
+		"isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+		oskb = skb;
+		skb = alloc_skb(oskb->len + i, GFP_ATOMIC);
+		SET_SKB_FREE(skb);
+		memcpy(skb_put(skb, i), header, i);
+		memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
+		FreeSkb(oskb);
+	}
+	st->l2.l2l1(st, PH_PULL | INDICATION, skb);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
+		FsmDelTimer(&st->l2.t203, 13);
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
 	}
 	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;
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 }
 
 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)
+l2_got_st8_super(struct FsmInst *fi, int event, void *arg)
 {
 	struct PStack *st = fi->userdata;
 	struct sk_buff *skb = arg;
-	int PollFlag, seq, rsp;
-	struct Layer2 *l2;
+	int PollFlag, nr, rsp, rnr = 0;
+	struct Layer2 *l2 = &st->l2;
 
-	l2 = &st->l2;
-	if (l2->laptype == LAPD)
-		rsp = *skb->data & 0x2;
-	else
-		rsp = *skb->data == 0x3;
-	if (l2->orig)
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
 		rsp = !rsp;
-
 	skb_pull(skb, l2addrsize(l2));
-	if (l2->extended) {
-		PollFlag = (skb->data[1] & 0x1) == 0x1;
-		seq = skb->data[1] >> 1;
+
+	if (IsRNR(skb->data, test_bit(FLG_MOD128, &l2->flag))) {
+		test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+		rnr = 1;
+	} else
+		test_and_clear_bit(FLG_PEER_BUSY, &l2->flag);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		if (skb->len == 2) {
+			PollFlag = (skb->data[1] & 0x1) == 0x1;
+			nr = skb->data[1] >> 1;
+		} else {
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
 	} else {
-		PollFlag = (skb->data[0] & 0x10);
-		seq = (skb->data[0] >> 5) & 0x7;
+		if (skb->len == 1) {
+			PollFlag = (skb->data[0] & 0x10);
+			nr = (skb->data[0] >> 5) & 0x7;
+		} else {
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+			FreeSkb(skb);
+			establishlink(fi);
+			return;
+		}
 	}
-	SET_SKB_FREE(skb);
-	dev_kfree_skb(skb, FREE_READ);
+	FreeSkb(skb);
 
 	if (rsp && PollFlag) {
-		if (legalnr(st, seq)) {
+		if (legalnr(st, nr)) {
+			setva(st, nr);
+			if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+				FsmDelTimer(&st->l2.t200, 7);
+			FsmDelTimer(&l2->t203, 8);
+			if (rnr) {
+				FsmRestartTimer(&l2->t200, l2->T200,
+						EV_L2_T200, NULL, 14);
+				test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+			} else
+				FsmAddTimer(&l2->t203, l2->T203,
+					    EV_L2_T203, NULL, 5);
+			invoke_retransmission(st, nr);
 			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);
+				st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
 			else if (fi->userint & LC_FLUSH_WAIT) {
 				fi->userint &= ~LC_FLUSH_WAIT;
-				st->l2.l2man(st, DL_FLUSH, NULL);
+				st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL);
 			}
 		}
 	} else {
 		if (!rsp && PollFlag)
-			enquiry_response(st, RR, PollFlag);
-		if (legalnr(st, seq)) {
-			setva(st, seq);
+			enquiry_response(st);
+		if (legalnr(st, nr)) {
+			setva(st, nr);
 		}
 	}
 }
 
 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_pull(skb, l2addrsize(&st->l2) + 1);
+	if (test_bit(FLG_MOD128, &st->l2.flag)) {
+		if (skb->len < 5)
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+		else
+			l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x",
 				skb->data[0], skb->data[1], skb->data[2],
 				skb->data[3], skb->data[4]);
+	} else {
+		if (skb->len < 3)
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
 		else
-			sprintf(tmp, "FRMR information %2x %2x %2x",
+			l2m_debug(&st->l2.l2m, "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);
+	if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) ||		/* I or S */
+	    (IsUA(skb->data, 0) && (fi->state == ST_L2_7))) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+	FreeSkb(skb);
 }
 
 static void
@@ -1026,135 +1297,139 @@
 {
 	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 */
+	discard_queue(&st->l2.i_queue);
+	discard_queue(&st->l2.ui_queue);
+	st->l2.tei = -1;
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 18);
+	FsmDelTimer(&st->l2.t203, 19);
+	if (fi->state != ST_L2_4)
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
 	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)
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
 {
-	u_char d = data[0] & ~0x10;
+	struct PStack *st = fi->userdata;
+	int rel = DL_RELEASE | INDICATION;
 	
-	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);
+	
+	discard_queue(&st->l2.i_queue);
+	discard_queue(&st->l2.ui_queue);
+	if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, 18);
+	FsmDelTimer(&st->l2.t203, 19);
+	clear_exception(&st->l2);
+	switch (fi->state) {
+		case ST_L2_1:
+			if (!test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				break;
+		case ST_L2_3:
+			st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+		case ST_L2_2:
+			FsmChangeState(fi, ST_L2_1);
+			break;
+		case ST_L2_6:
+			rel = DL_RELEASE | CONFIRM;
+		case ST_L2_5:
+			if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+				rel = DL_RELEASE | CONFIRM;
+		case ST_L2_7:
+		case ST_L2_8:
+			st->l2.l2l3(st, rel, NULL);
+			FsmChangeState(fi, ST_L2_4);
+			break;
+		case ST_L2_4:
+			if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+			break;
+	}
+	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+	test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
 }
 
-static struct FsmNode L2FnList[] =
+static struct FsmNode L2FnList[] HISAX_INITDATA =
 {
-	{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_1, EV_L2_DL_ESTABLISH, l2_dl_establish},
+	{ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish},
 	{ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish},
-	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_5, EV_L2_DL_ESTABLISH, l2_establish},
+	{ST_L2_7, EV_L2_DL_ESTABLISH, l2_establish},
+	{ST_L2_8, EV_L2_DL_ESTABLISH, l2_establish},
+	{ST_L2_4, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_5, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_7, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_8, EV_L2_DL_RELEASE, l2_dl_release},
+	{ST_L2_5, EV_L2_DL_DATA, l2_feed_iqueue},
 	{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_1, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_2, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_3, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_5, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_6, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_8, EV_L2_DL_UNIT_DATA, l2_put_ui},
+	{ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ERROR, l2_tei_remove},
+	{ST_L2_3, EV_L2_MDL_ERROR, 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},
 	{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_5, EV_L2_SABMX, l2_got_SABMX},
+	{ST_L2_6, EV_L2_SABMX, l2_got_SABMX},
 	{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_4, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_5, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_6, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_7, EV_L2_DISC, l2_got_disconn},
 	{ST_L2_8, EV_L2_DISC, l2_got_disconn},
+	{ST_L2_4, EV_L2_UA, l2_mdl_error},
+	{ST_L2_5, EV_L2_UA, l2_got_ua},
+	{ST_L2_6, EV_L2_UA, l2_got_ua},
+	{ST_L2_7, EV_L2_UA, l2_mdl_error},
+	{ST_L2_8, EV_L2_UA, l2_mdl_error},
+	{ST_L2_4, EV_L2_DM, l2_got_dm},
+	{ST_L2_5, EV_L2_DM, l2_got_dm},
+	{ST_L2_6, EV_L2_DM, l2_got_dm},
+	{ST_L2_7, EV_L2_DM, l2_got_dm},
+	{ST_L2_8, EV_L2_DM, l2_got_dm},
+	{ST_L2_1, EV_L2_UI, l2_got_ui},
+	{ST_L2_2, EV_L2_UI, l2_got_ui},
+	{ST_L2_3, EV_L2_UI, l2_got_ui},
+	{ST_L2_4, EV_L2_UI, l2_got_ui},
+	{ST_L2_5, EV_L2_UI, l2_got_ui},
+	{ST_L2_6, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_UI, l2_got_ui},
+	{ST_L2_8, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
 	{ST_L2_8, EV_L2_FRMR, l2_got_FRMR},
-	{ST_L2_8, EV_L2_I, l2_got_st8_data},
-
+	{ST_L2_7, EV_L2_SUPER, l2_got_st7_super},
+	{ST_L2_8, EV_L2_SUPER, l2_got_st8_super},
+	{ST_L2_7, EV_L2_I, l2_got_iframe},
+	{ST_L2_8, EV_L2_I, l2_got_iframe},
 	{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_T200, l2_st78_tout_200},
+	{ST_L2_8, EV_L2_T200, l2_st78_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},
+	{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+	{ST_L2_1, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_5, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_6, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
 };
 
 #define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
@@ -1164,40 +1439,69 @@
 {
 	struct sk_buff *skb = arg;
 	u_char *datap;
-	int ret = !0;
+	int ret = 1, len;
 
 	switch (pr) {
-		case (PH_DATA):
+		case (PH_DATA | INDICATION):
 			datap = skb->data;
-			datap += l2addrsize(&st->l2);
-
-			if (IsI(datap, st->l2.extended))
+			len = l2addrsize(&st->l2);
+			if (skb->len > len)
+				datap += len;
+			else {
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+				FreeSkb(skb);
+				return;
+			}
+			if (!(*datap & 1))	/* I-Frame */
 				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))
+			else if (IsSFrame(datap, test_bit(FLG_MOD128, &st->l2.flag)))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb);
+			else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
-			else if (IsSABMX(datap, st->l2.extended))
+			else if (IsSABMX(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, skb);
-			else if (IsUA(datap, st->l2.extended))
+			else if (IsUA(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb);
-			else if (IsDISC(datap, st->l2.extended))
+			else if (IsDISC(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				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))
+			else if (IsDM(datap, test_bit(FLG_MOD128, &st->l2.flag)))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb);
+			else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag)))
 				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);
-
+			else {
+				ret = 1;
+				if ((st->l2.l2m.state == ST_L2_7) ||
+					(st->l2.l2m.state == ST_L2_8))
+					establishlink(&st->l2.l2m);
+				st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
+			}
 			if (ret) {
-				SET_SKB_FREE(skb);
-				dev_kfree_skb(skb, FREE_READ);
+				FreeSkb(skb);
 			}
 			break;
-		case (PH_PULL_ACK):
+		case (PH_PULL | CONFIRM):
 			FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
 			break;
+		case (PH_PAUSE | INDICATION):
+			test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
+		case (PH_PAUSE | CONFIRM):
+			test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
+		case (PH_ACTIVATE | CONFIRM):
+		case (PH_ACTIVATE | INDICATION):
+			test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag);
+			if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
+			FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
+			break;
+		default:
+			l2m_debug(&st->l2.l2m, "l2 unknown pr %04x", pr);
+			break;
 	}
 }
 
@@ -1205,69 +1509,70 @@
 isdnl2_l3l2(struct PStack *st, int pr, void *arg)
 {
 	switch (pr) {
-		case (DL_DATA):
+		case (DL_DATA | REQUEST):
 			if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
 				dev_kfree_skb((struct sk_buff *) arg, FREE_READ);
 			}
 			break;
-		case (DL_UNIT_DATA):
+		case (DL_UNIT_DATA | REQUEST):
 			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);
+		case (DL_ESTABLISH | REQUEST):
+			if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+				}
+			} else {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
+				}
+				st->l2.l2l1(st, PH_ACTIVATE, NULL);
+			}
 			break;
-		case (DL_RELEASE):
+		case (DL_RELEASE | REQUEST):
+			if (test_bit(FLG_LAPB, &st->l2.flag)) {
+				st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+			}
 			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):
+		case (DL_FLUSH | REQUEST):
 			(&st->l2.l2m)->userint |= LC_FLUSH_WAIT;
 			break;
-	}
-}
-
-static void
-isdnl2_teil2(struct PStack *st, int pr, void *arg)
-{
-	switch (pr) {
-		case (MDL_ASSIGN):
+		case (MDL_ASSIGN | REQUEST):
 			FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
 			break;
-		case (MDL_REMOVE):
+		case (MDL_REMOVE | REQUEST):
 			FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
 			break;
+		case (MDL_ERROR | RESPONSE):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg);
+			break;
 	}
 }
 
 void
 releasestack_isdnl2(struct PStack *st)
 {
-	FsmDelTimer(&st->l2.t200_timer, 15);
-	FsmDelTimer(&st->l2.t203_timer, 16);
-	discard_i_queue(st);
+	FsmDelTimer(&st->l2.t200, 15);
+	FsmDelTimer(&st->l2.t203, 16);
+	discard_queue(&st->l2.i_queue);
+	discard_queue(&st->l2.ui_queue);
 	ReleaseWin(&st->l2);
 }
 
 static void
-l2m_debug(struct FsmInst *fi, char *s)
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
 {
+	va_list args;
 	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);
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l2.debug_id, fmt, args);
+	va_end(args);
 }
 
 void
@@ -1275,17 +1580,16 @@
 {
 	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);
+	skb_queue_head_init(&st->l2.ui_queue);
 	InitWin(&st->l2);
-	st->l2.rejexp = 0;
 	st->l2.debug = 0;
 
 	st->l2.l2m.fsm = &l2fsm;
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l2.l2m.state = ST_L2_4;
+	else
 	st->l2.l2m.state = ST_L2_1;
 	st->l2.l2m.debug = 0;
 	st->l2.l2m.userdata = st;
@@ -1293,14 +1597,31 @@
 	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;
+	FsmInitTimer(&st->l2.l2m, &st->l2.t200);
+	FsmInitTimer(&st->l2.l2m, &st->l2.t203);
+}
+
+static void
+transl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+		case (DL_UNIT_DATA | REQUEST):
+			st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+			break;
+	}
 }
 
 void
 setstack_transl2(struct PStack *st)
 {
+	st->l3.l3l2 = transl2_l3l2;
 }
 
 void
@@ -1308,8 +1629,8 @@
 {
 }
 
-void
-Isdnl2New(void)
+HISAX_INITFUNC(void
+Isdnl2New(void))
 {
 	l2fsm.state_count = L2_STATE_COUNT;
 	l2fsm.event_count = L2_EVENT_COUNT;
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c
--- v2.0.35/linux/drivers/isdn/hisax/isdnl3.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/isdnl3.c	Sun Nov 15 10:32:58 1998
@@ -1,44 +1,51 @@
-/* $Id: isdnl3.c,v 1.10 1997/04/06 22:54:16 keil Exp $
+/* $Id: isdnl3.c,v 1.10.2.6 1998/11/03 00:07:06 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * 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.10.2.6  1998/11/03 00:07:06  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.9  1997/03/25 23:11:25  keil
- * US NI-1 protocol
+ * Revision 1.10.2.5  1998/09/27 13:06:39  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
- * Revision 1.8  1997/03/21 18:53:44  keil
- * Report no protocol error to syslog too
+ * Revision 1.10.2.4  1998/05/27 18:05:59  keil
+ * HiSax 3.0
  *
- * Revision 1.7  1997/03/17 18:34:38  keil
- * fixed oops if no protocol selected during config
+ * Revision 1.10.2.3  1997/11/15 18:54:09  keil
+ * cosmetics
  *
- * Revision 1.6  1997/02/16 01:04:08  fritz
- * Bugfix: Changed timer handling caused hang with 2.1.X
+ * Revision 1.10.2.2  1997/10/17 22:14:05  keil
+ * update to last hisax version
  *
- * Revision 1.5  1997/02/09 00:26:27  keil
- * new interface handling, one interface per card
- * leased line changes
+ * Revision 2.1  1997/08/03 14:36:32  keil
+ * Implement RESTART procedure
  *
- * Revision 1.4  1997/01/27 23:17:44  keil
- * delete timers while unloading
+ * Revision 2.0  1997/07/27 21:15:42  keil
+ * New Callref based layer3
  *
- * Revision 1.3  1997/01/21 22:31:12  keil
- * new statemachine; L3 timers
+ * Revision 1.11  1997/06/26 11:11:44  keil
+ * SET_SKBFREE now on creation of a SKB
  *
- * Revision 1.2  1996/11/05 19:42:04  keil
- * using config.h
+ * Revision 1.10  1997/04/06 22:54:16  keil
+ * Using SKB's
  *
- * Revision 1.1  1996/10/13 20:04:54  keil
- * Initial revision
+ * 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
  *
+ * Remove old logs /KKe
  *
  */
 #define __NO_VERSION__
@@ -46,42 +53,145 @@
 #include "isdnl3.h"
 #include <linux/config.h>
 
-const char *l3_revision = "$Revision: 1.10 $";
+const char *l3_revision = "$Revision: 1.10.2.6 $";
 
-void
-l3_debug(struct PStack *st, char *s)
+static
+struct Fsm l3fsm =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L3_LC_REL,
+	ST_L3_LC_ESTAB_WAIT,
+	ST_L3_LC_REL_WAIT,
+	ST_L3_LC_ESTAB,
+};
+
+#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1)
+
+static char *strL3State[] =
+{
+	"ST_L3_LC_REL",
+	"ST_L3_LC_ESTAB_WAIT",
+	"ST_L3_LC_REL_WAIT",
+	"ST_L3_LC_ESTAB",
+};
+
+enum {
+	EV_ESTABLISH_REQ,
+	EV_ESTABLISH_IND,
+	EV_ESTABLISH_CNF,
+	EV_RELEASE_REQ,
+	EV_RELEASE_CNF,
+	EV_RELEASE_IND,
+};
+
+#define L3_EVENT_COUNT (EV_RELEASE_IND+1)
+
+static char *strL3Event[] =
+{
+	"EV_ESTABLISH_REQ",
+	"EV_ESTABLISH_IND",
+	"EV_ESTABLISH_CNF",
+	"EV_RELEASE_REQ",
+	"EV_RELEASE_CNF",
+	"EV_RELEASE_IND",
+};
+
+static void
+l3m_debug(struct FsmInst *fi, char *fmt, ...)
 {
-	char str[256], tm[32];
+	va_list args;
+	struct PStack *st = fi->userdata;
 
-	jiftime(tm, jiffies);
-	sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s);
-	HiSax_putstatus(st->l1.hardware, str);
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args);
+	va_end(args);
 }
 
+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
-newl3state(struct PStack *st, int state)
+int
+getcallref(u_char * p)
 {
-	char tmp[80];
-
-	if (st->l3.debug & L3_DEB_STATE) {
-		sprintf(tmp, "newstate  %d --> %d", st->l3.state, state);
-		l3_debug(st, tmp);
+	int l, m = 1, cr = 0;
+	p++;			/* prot discr */
+	l = 0xf & *p++;		/* callref length */
+	if (!l)			/* dummy CallRef */
+		return(-1);
+	while (l--) {
+		cr += m * (*p++);
+		m *= 8;
 	}
-	st->l3.state = state;
+	return (cr);
+}
+
+static int OrigCallRef = 0;
+
+int
+newcallref(void)
+{
+	if (OrigCallRef == 127)
+		OrigCallRef = 1;
+	else
+		OrigCallRef++;
+	return (OrigCallRef);
+}
+
+void
+newl3state(struct l3_process *pc, int state)
+{
+	if (pc->debug & L3_DEB_STATE)
+		l3_debug(pc->st, "newstate cr %d %d --> %d", pc->callref,
+			pc->state, state);
+	pc->state = state;
 }
 
 static void
 L3ExpireTimer(struct L3Timer *t)
 {
-	t->st->l4.l4l3(t->st, t->event, NULL);
+	t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
 }
 
 void
-L3InitTimer(struct PStack *st, struct L3Timer *t)
+L3InitTimer(struct l3_process *pc, struct L3Timer *t)
 {
-	t->st = st;
+	t->pc = pc;
 	t->tl.function = (void *) L3ExpireTimer;
 	t->tl.data = (long) t;
 	init_timer(&t->tl);
@@ -109,9 +219,9 @@
 }
 
 void
-StopAllL3Timer(struct PStack *st)
+StopAllL3Timer(struct l3_process *pc)
 {
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->timer);
 }
 
 struct sk_buff *
@@ -133,9 +243,10 @@
 {
 	struct sk_buff *skb = arg;
 
-	l3_debug(st, "no protocol");
-	if (skb)
+	HiSax_putstatus(st->l1.hardware, "L3", "no D protocol");
+	if (skb) {
 		dev_kfree_skb(skb, FREE_READ);
+	}
 }
 
 #ifdef	CONFIG_HISAX_EURO
@@ -150,14 +261,87 @@
 extern void setstack_1tr6(struct PStack *st);
 #endif
 
+struct l3_process
+*getl3proc(struct PStack *st, int cr)
+{
+	struct l3_process *p = st->l3.proc;
+
+	while (p)
+		if (p->callref == cr)
+			return (p);
+		else
+			p = p->next;
+	return (NULL);
+}
+
+struct l3_process
+*new_l3_process(struct PStack *st, int cr)
+{
+	struct l3_process *p, *np;
+
+	if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr);
+		return (NULL);
+	}
+	if (!st->l3.proc)
+		st->l3.proc = p;
+	else {
+		np = st->l3.proc;
+		while (np->next)
+			np = np->next;
+		np->next = p;
+	}
+	p->next = NULL;
+	p->debug = L3_DEB_WARN;
+	p->callref = cr;
+	p->state = 0;
+	p->chan = NULL;
+	p->st = st;
+	p->N303 = st->l3.N303;
+	L3InitTimer(p, &p->timer);
+	return (p);
+};
+
+void
+release_l3_process(struct l3_process *p)
+{
+	struct l3_process *np, *pp = NULL;
+
+	if (!p)
+		return;
+	np = p->st->l3.proc;
+	while (np) {
+		if (np == p) {
+			StopAllL3Timer(p);
+			if (pp)
+				pp->next = np->next;
+			else
+				p->st->l3.proc = np->next;
+			kfree(p);
+			return;
+		}
+		pp = np;
+		np = np->next;
+	}
+	printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref);
+	l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref);
+};
+
 void
-setstack_isdnl3(struct PStack *st, struct Channel *chanp)
+setstack_l3dc(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);
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+	strcpy(st->l3.debug_id, "L3DC ");
 
 #ifdef	CONFIG_HISAX_EURO
 	if (st->protocol == ISDN_PTYPE_EURO) {
@@ -175,11 +359,11 @@
 	} else
 #endif
 	if (st->protocol == ISDN_PTYPE_LEASED) {
-		st->l4.l4l3 = no_l3_proto;
+		st->lli.l4l3 = no_l3_proto;
 		st->l2.l2l3 = no_l3_proto;
-		printk(KERN_NOTICE "HiSax: Leased line mode\n");
+		printk(KERN_INFO "HiSax: Leased line mode\n");
 	} else {
-		st->l4.l4l3 = no_l3_proto;
+		st->lli.l4l3 = no_l3_proto;
 		st->l2.l2l3 = no_l3_proto;
 		sprintf(tmp, "protocol %s not supported",
 			(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
@@ -187,15 +371,155 @@
 			(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
+isdnl3_trans(struct PStack *st, int pr, void *arg) {
+	st->l3.l3l2(st, pr, arg);
 }
 
 void
 releasestack_isdnl3(struct PStack *st)
 {
-	StopAllL3Timer(st);
+	while (st->l3.proc)
+		release_l3_process(st->l3.proc);
+	if (st->l3.global) {
+		StopAllL3Timer(st->l3.global);
+		kfree(st->l3.global);
+		st->l3.global = NULL;
+	}
+	discard_queue(&st->l3.squeue);
+}
+
+void
+setstack_l3bc(struct PStack *st, struct Channel *chanp)
+{
+
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+	strcpy(st->l3.debug_id, "L3BC ");
+	st->lli.l4l3 = isdnl3_trans;
+}
+
+static void
+lc_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
+	st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lc_connect(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB);
+	while ((skb = skb_dequeue(&st->l3.squeue))) {
+		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+	}
+	st->l3.l3l4(st, DL_ESTABLISH | INDICATION, NULL);
+}
+
+static void
+lc_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (fi->state == ST_L3_LC_ESTAB_WAIT)
+		FsmChangeState(fi, ST_L3_LC_REL);
+	else
+		FsmChangeState(fi, ST_L3_LC_REL_WAIT);
+	st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+}
+
+static void
+lc_release_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_REL);
+	discard_queue(&st->l3.squeue);
+	st->l3.l3l4(st, DL_RELEASE | INDICATION, NULL);
+}
+
+/* *INDENT-OFF* */
+static struct FsmNode L3FnList[] HISAX_INITDATA =
+{
+	{ST_L3_LC_REL,		EV_ESTABLISH_REQ,	lc_activate},
+	{ST_L3_LC_REL,		EV_ESTABLISH_IND,	lc_connect},
+	{ST_L3_LC_REL,		EV_ESTABLISH_CNF,	lc_connect},
+	{ST_L3_LC_ESTAB_WAIT,	EV_ESTABLISH_CNF,	lc_connect},
+	{ST_L3_LC_ESTAB_WAIT,	EV_RELEASE_REQ,		lc_release_req},
+	{ST_L3_LC_ESTAB_WAIT,	EV_RELEASE_IND,		lc_release_ind},
+	{ST_L3_LC_ESTAB,	EV_RELEASE_IND,		lc_release_ind},
+	{ST_L3_LC_ESTAB,	EV_RELEASE_REQ,		lc_release_req},
+	{ST_L3_LC_REL_WAIT,	EV_RELEASE_CNF,		lc_release_ind},
+	{ST_L3_LC_REL_WAIT,	EV_ESTABLISH_REQ,	lc_activate},
+};
+/* *INDENT-ON* */
+
+#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode))
+
+void
+l3_msg(struct PStack *st, int pr, void *arg)
+{
+	
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
+				st->l3.l3l2(st, pr, arg);
+			} else {
+				struct sk_buff *skb = arg;
+
+				skb_queue_head(&st->l3.squeue, skb);
+				FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); 
+			}
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL);
+			break;
+		case (DL_ESTABLISH | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL);
+			break;
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+			break;
+	}
+}
+
+HISAX_INITFUNC(void
+Isdnl3New(void))
+{
+	l3fsm.state_count = L3_STATE_COUNT;
+	l3fsm.event_count = L3_EVENT_COUNT;
+	l3fsm.strEvent = strL3Event;
+	l3fsm.strState = strL3State;
+	FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+}
+
+void
+Isdnl3Free(void)
+{
+	FsmFree(&l3fsm);
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h
--- v2.0.35/linux/drivers/isdn/hisax/isdnl3.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/isdnl3.h	Sun Nov 15 10:32:58 1998
@@ -1,6 +1,22 @@
-/* $Id: isdnl3.h,v 1.3 1997/04/06 22:54:17 keil Exp $
- *
+/* $Id: isdnl3.h,v 1.3.2.3 1998/11/03 00:07:08 keil Exp $
+
  * $Log: isdnl3.h,v $
+ * Revision 1.3.2.3  1998/11/03 00:07:08  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.3.2.2  1998/05/27 18:06:02  keil
+ * HiSax 3.0
+ *
+ * Revision 1.3.2.1  1997/10/17 22:14:08  keil
+ * update to last hisax version
+ *
+ * Revision 2.0  1997/07/27 21:15:42  keil
+ * New Callref based layer3
+ *
+ * Revision 1.4  1997/06/26 11:20:57  keil
+ * ?
+ *
  * Revision 1.3  1997/04/06 22:54:17  keil
  * Using SKB's
  *
@@ -24,15 +40,20 @@
 #define	L3_DEB_CHARGE	0x08
 
 struct stateentry {
-	int	state;
-	u_char	primitive;
-	void	(*rout) (struct PStack *, u_char, void *);
+	int state;
+	int primitive;
+	void (*rout) (struct l3_process *, 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);
+#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args)
+
+extern void newl3state(struct l3_process *pc, int state);
+extern void L3InitTimer(struct l3_process *pc, 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 int L3AddTimer(struct L3Timer *t, int millisec, int event);
+extern void StopAllL3Timer(struct l3_process *pc);
 extern struct sk_buff *l3_alloc_skb(int len);
+extern struct l3_process *new_l3_process(struct PStack *st, int cr);
+extern void release_l3_process(struct l3_process *p);
+extern struct l3_process *getl3proc(struct PStack *st, int cr);
+extern void l3_msg(struct PStack *st, int pr, void *arg);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/ix1_micro.c linux/drivers/isdn/hisax/ix1_micro.c
--- v2.0.35/linux/drivers/isdn/hisax/ix1_micro.c	Tue Aug  5 09:00:12 1997
+++ linux/drivers/isdn/hisax/ix1_micro.c	Sun Nov 15 10:32:58 1998
@@ -1,4 +1,4 @@
-/* $Id: ix1_micro.c,v 1.3 1997/04/13 19:54:02 keil Exp $
+/* $Id: ix1_micro.c,v 1.3.2.8 1998/04/08 21:58:41 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
@@ -11,6 +11,27 @@
  *              Beat Doebeli
  *
  * $Log: ix1_micro.c,v $
+ * Revision 1.3.2.8  1998/04/08 21:58:41  keil
+ * New init code
+ *
+ * Revision 1.3.2.7  1998/02/11 14:21:01  keil
+ * cosmetic fixes
+ *
+ * Revision 1.3.2.6  1998/01/27 22:37:33  keil
+ * fast io
+ *
+ * Revision 1.3.2.5  1997/11/15 18:50:51  keil
+ * new common init function
+ *
+ * Revision 1.3.2.4  1997/10/17 22:14:09  keil
+ * update to last hisax version
+ *
+ * Revision 2.1  1997/07/27 21:47:09  keil
+ * new interface structures
+ *
+ * Revision 2.0  1997/06/26 11:02:50  keil
+ * New Layer and card interface
+ *
  * Revision 1.3  1997/04/13 19:54:02  keil
  * Change in IRQ check delay for SMP
  *
@@ -54,17 +75,16 @@
 
 
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "teles3.h"
+#include "isac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
 
 extern const char *CardType[];
-const char *ix1_revision = "$Revision: 1.3 $";
+const char *ix1_revision = "$Revision: 1.3.2.8 $";
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 #define SPECIAL_PORT_OFFSET 3
 
@@ -73,865 +93,247 @@
 #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)
+readreg(unsigned int ale, unsigned int adr, 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);
-}
+	register u_char ret;
+	long flags;
 
-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++;
-	}
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
 }
 
 static inline void
-waitforCEC(int adr, int WhichHscx)
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
-	int to = TIMEOUT;
+	/* fifo read without cli because it's allready done  */
 
-	while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) {
-		udelay(1);
-		to--;
-	}
-	if (!to)
-		printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n");
+	byteout(ale, off);
+	insb(adr, data, size);
 }
 
 
 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)
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
 {
 	long flags;
 
 	save_flags(flags);
 	cli();
-	waitforCEC(adr, WhichHscx);
-	HscxWriteReg(adr, WhichHscx, HSCX_CMDR, data);
+	byteout(ale, off);
+	byteout(adr, 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)
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
 {
-	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);
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
 }
 
-/*
- * HSCX stuff goes here
- */
+/* Interface functions */
 
-static void
-hscx_empty_fifo(struct HscxState *hsp, int count)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	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);
-	}
+	return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset));
 }
 
 static void
-hscx_fill_fifo(struct HscxState *hsp)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	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);
-	}
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value);
 }
 
-static inline void
-hscx_interrupt(struct IsdnCardState *sp, u_char val, u_char hscx)
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
 }
 
-/*
- * ISAC stuff goes here
- */
-
 static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
 }
 
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	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);
-	}
+	return (readreg(cs->hw.ix1.hscx_ale,
+			cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0)));
 }
 
 static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	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);
+	writereg(cs->hw.ix1.hscx_ale,
+		 cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value);
 }
 
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data)
 
-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);
-		}
-	}
-}
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
 
-	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);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, stat = 0;
 
-	sp = (struct IsdnCardState *) irq2dev_map[intno];
-
-	if (!sp) {
-		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+	if (!cs) {
+		printk(KERN_WARNING "IX1: Spurious interrupt!\n");
 		return;
 	}
-	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 		stat |= 1;
 	}
-	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, val);
 		stat |= 2;
 	}
-	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
 	if (val) {
-		if (sp->debug & L1_DEB_HSCX)
-			debugl1(sp, "HSCX IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
 		goto Start_HSCX;
 	}
-	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
 	if (val) {
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "ISAC IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "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);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0);
+		writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0);
 	}
 	if (stat & 2) {
-		IsacWriteReg(sp->isac, ISAC_MASK, 0xFF);
-		IsacWriteReg(sp->isac, ISAC_MASK, 0x0);
+		writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0);
 	}
 }
 
-
-static void
-initisac(struct IsdnCardState *sp)
+void
+release_io_ix1micro(struct IsdnCardState *cs)
 {
-	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);
+	if (cs->hw.ix1.cfg_reg)
+		release_region(cs->hw.ix1.cfg_reg, 4);
 }
 
 static void
-modehscx(struct HscxState *hs, int mode, int ichan)
+ix1_reset(struct IsdnCardState *cs)
 {
-	struct IsdnCardState *sp = hs->sp;
-	int hscx = hs->hscx;
+	long flags;
+	int cnt;
 
-	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;
+	/* reset isac */
+	save_flags(flags);
+	cnt = 3 * (HZ / 10) + 1;
+	sti();
+	while (cnt--) {
+		byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1);
+		HZDELAY(1);	/* wait >=10 ms */
 	}
-	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);
+	byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0);
+	restore_flags(flags);
 }
 
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	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);
-		}
+	switch (mt) {
+		case CARD_RESET:
+			ix1_reset(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_ix1micro(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &ix1micro_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_ix1micro(struct IsdnCard *card)
+
+__initfunc(int
+setup_ix1micro(struct IsdnCard *card))
 {
-	u_char val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
-	long flags;
+	struct IsdnCardState *cs = card->cs;
 	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)
+	printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->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)) {
+	cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET;
+	cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET;
+	cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET;
+	cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET;
+	cs->hw.ix1.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (cs->hw.ix1.cfg_reg) {
+		if (check_region((cs->hw.ix1.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);
+			       cs->hw.ix1.cfg_reg,
+			       cs->hw.ix1.cfg_reg + 4);
 			return (0);
 		} else
-			request_region(sp->cfg_reg, 4, "ix1micro cfg");
+			request_region(cs->hw.ix1.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_INFO
+	       "HiSax: %s config irq:%d io:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.ix1.cfg_reg);
+	ix1_reset(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &ix1_card_msg;
+	ISACVersion(cs, "ix1-Micro:");
+	if (HscxVersion(cs, "ix1-Micro:")) {
 		printk(KERN_WARNING
 		    "ix1-Micro: wrong HSCX versions check IO address\n");
-		release_io_ix1micro(card);
+		release_io_ix1micro(cs);
 		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.35/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- v2.0.35/linux/drivers/isdn/hisax/l3_1tr6.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Sun Nov 15 10:32:58 1998
@@ -1,44 +1,47 @@
-/* $Id: l3_1tr6.c,v 1.11 1997/04/06 22:54:18 keil Exp $
+/* $Id: l3_1tr6.c,v 1.11.2.6 1998/11/03 00:07:10 keil Exp $
 
  *  German 1TR6 D-channel protocol
  *
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
  *
- * $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
+ * $Log: l3_1tr6.c,v $
+ * Revision 1.11.2.6  1998/11/03 00:07:10  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.9  1997/02/11 01:37:40  keil
- * Changed setup-interface (incoming and outgoing)
+ * Revision 1.11.2.5  1998/10/25 18:16:21  fritz
+ * Replaced some read-only variables by defines.
  *
- * Revision 1.8  1997/01/27 23:20:21  keil
- * report revision only ones
+ * Revision 1.11.2.4  1998/09/27 13:06:42  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
- * Revision 1.7  1997/01/21 22:30:07  keil
- * new statemachine; L3 timers
+ * Revision 1.11.2.3  1998/05/27 18:06:04  keil
+ * HiSax 3.0
  *
- * Revision 1.6  1996/12/14 21:07:20  keil
- * additional states for CC_REJECT
+ * Revision 1.11.2.2  1997/11/15 18:54:12  keil
+ * cosmetics
  *
- * Revision 1.5  1996/12/08 19:55:17  keil
- * change CC_REJECT_REQ routine
+ * Revision 1.11.2.1  1997/10/17 22:14:12  keil
+ * update to last hisax version
  *
- * Revision 1.4  1996/10/30 10:18:01  keil
- * bugfixes in debugging output
+ * Revision 2.1  1997/08/03 15:28:09  keil
+ * release L3 empty processes
  *
- * Revision 1.3  1996/10/27 22:15:37  keil
- * bugfix reject handling
+ * Revision 2.0  1997/07/27 21:15:45  keil
+ * New Callref based layer3
  *
- * Revision 1.2  1996/10/13 23:08:56  keil
- * added missing state for callback reject
+ * Revision 1.12  1997/06/26 11:11:45  keil
+ * SET_SKBFREE now on creation of a SKB
  *
- * Revision 1.1  1996/10/13 20:04:55  keil
- * Initial revision
+ * Revision 1.11  1997/04/06 22:54:18  keil
+ * Using SKB's
  *
+ * Old Log removed /KKe
  *
  */
 
@@ -49,16 +52,16 @@
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *l3_1tr6_revision = "$Revision: 1.11 $";
+const char *l3_1tr6_revision = "$Revision: 1.11.2.6 $";
 
 #define MsgHead(ptr, cref, mty, dis) \
 	*ptr++ = dis; \
 	*ptr++ = 0x1; \
-	*ptr++ = cref; \
+	*ptr++ = cref ^ 0x80; \
 	*ptr++ = mty
 
 static void
-l3_1TR6_message(struct PStack *st, u_char mt, u_char pd)
+l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd)
 {
 	struct sk_buff *skb;
 	u_char *p;
@@ -66,12 +69,39 @@
 	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);
+	MsgHead(p, pc->callref, mt, pd);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb, FREE_READ);
+	l3_1tr6_release_req(pc, 0, NULL);
 }
 
 static void
-l3_1tr6_setup_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb)
+{
+	dev_kfree_skb(skb, FREE_READ);
+	if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, msg);
+	l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[128];
@@ -81,16 +111,13 @@
 	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;
+	MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1);
+	teln = pc->para.setup.phone;
+	pc->para.spv = 0;
 	if (!isdigit(*teln)) {
 		switch (0x5f & *teln) {
 			case 'S':
-				st->pa->spv = 1;
+				pc->para.spv = 1;
 				break;
 			case 'C':
 				channel = 0x08;
@@ -103,8 +130,8 @@
 					channel |= 0x02;
 				break;
 			default:
-				if (st->l3.debug & L3_DEB_WARN)
-					l3_debug(st, "Wrong MSN Code");
+				if (pc->st->l3.debug & L3_DEB_WARN)
+					l3_debug(pc->st, "Wrong MSN Code");
 				break;
 		}
 		teln++;
@@ -114,22 +141,22 @@
 		*p++ = 1;
 		*p++ = channel;
 	}
-	if (st->pa->spv) {	/* SPV ? */
+	if (pc->para.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++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.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 */
+		*p++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.setup.si2;	/* 0 for all Services */
 	}
-	eaz = st->pa->setup.eazmsn;
+	eaz = pc->para.setup.eazmsn;
 	if (*eaz) {
 		*p++ = WE0_origAddr;
 		*p++ = strlen(eaz) + 1;
@@ -149,22 +176,21 @@
 	/* Codesatz 6 fuer Service */
 	*p++ = WE6_serviceInd;
 	*p++ = 2;		/* len=2 info,info2 */
-	*p++ = st->pa->setup.si1;
-	*p++ = st->pa->setup.si2;
+	*p++ = pc->para.setup.si1;
+	*p++ = pc->para.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);
-
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
-l3_1tr6_setup(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	int bcfound = 0;
@@ -172,110 +198,141 @@
 	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");
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->para.bchannel = p[2] & 0x3))
+				bcfound++;
+	} else {
+		l3_1tr6_error(pc, "missing setup chanID", skb);
+		return;
+	}
 
 	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");
+		pc->para.setup.si1 = p[2];
+		pc->para.setup.si2 = p[3];
+	} else {
+		l3_1tr6_error(pc, "missing setup SI", skb);
+		return;
+	}
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_destAddr, 0)))
-		iecpy(st->pa->setup.eazmsn, p, 1);
+		iecpy(pc->para.setup.eazmsn, p, 1);
 	else
-		st->pa->setup.eazmsn[0] = 0;
+		pc->para.setup.eazmsn[0] = 0;
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_origAddr, 0))) {
-		iecpy(st->pa->setup.phone, p, 1);
+		iecpy(pc->para.setup.phone, p, 1);
 	} else
-		st->pa->setup.phone[0] = 0;
+		pc->para.setup.phone[0] = 0;
 
 	p = skb->data;
-	st->pa->spv = 0;
+	pc->para.spv = 0;
 	if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) {
 		if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
-			st->pa->spv = 1;
+			pc->para.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)) {
+		if ((pc->para.setup.si1 != 7) && (pc->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);
+				pc->para.setup.phone,
+				pc->para.setup.eazmsn);
+			l3_debug(pc->st, tmp);
 		}
-		newl3state(st, 6);
-		st->l3.l3l4(st, CC_SETUP_IND, NULL);
-	}
+		newl3state(pc, 6);
+		pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+	} else
+		release_l3_process(pc);
 }
 
 static void
-l3_1tr6_setup_ack(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->timer);
 	p = skb->data;
-	newl3state(st, 2);
+	newl3state(pc, 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);
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup_ack wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup_ack wrong WE0_chanID", skb);
+			return;
+		}
+		pc->para.bchannel = p[2] & 0x3;
+	} else {
+		l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb);
+		return;
+	}
 	dev_kfree_skb(skb, FREE_READ);
-	L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304);
-	st->l3.l3l4(st, CC_MORE_INFO, NULL);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
 }
 
 static void
-l3_1tr6_call_sent(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->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);
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "call sent wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "call sent wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->state == 2) && (pc->para.bchannel != (p[2] & 0x3))) {
+			l3_1tr6_error(pc, "call sent wrong chanID value", skb);
+			return;
+		}
+		pc->para.bchannel = p[2] & 0x3;
+	} else {
+		l3_1tr6_error(pc, "missing call sent WE0_chanID", skb);
+		return;
+	}
 	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);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	newl3state(pc, 3);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
 }
 
 static void
-l3_1tr6_alert(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_alert(struct l3_process *pc, 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);
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
 }
 
 static void
-l3_1tr6_info(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	int i, tmpcharge = 0;
@@ -289,45 +346,46 @@
 			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 (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
 		}
-		if (st->l3.debug & L3_DEB_CHARGE) {
-			sprintf(tmp, "charging info %d", st->pa->chargeinfo);
-			l3_debug(st, tmp);
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
 		}
-	} else if (st->l3.debug & L3_DEB_CHARGE)
-		l3_debug(st, "charging info not found");
-	SET_SKB_FREE(skb);
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->st, "charging info not found");
 	dev_kfree_skb(skb, FREE_READ);
 
 }
 
 static void
-l3_1tr6_info_s2(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_info_s2(struct l3_process *pc, 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)
+l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);	/* T310 */
-	newl3state(st, 10);
-	SET_SKB_FREE(skb);
+	L3DelTimer(&pc->timer);	/* T310 */
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connect date", skb);
+		return;
+	}
+	newl3state(pc, 10);
 	dev_kfree_skb(skb, FREE_READ);
-	st->pa->chargeinfo = 0;
-	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+	pc->para.chargeinfo = 0;
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
 }
 
 static void
-l3_1tr6_rel(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 	u_char *p;
@@ -335,47 +393,50 @@
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE0_cause, 0))) {
 		if (p[1] > 0) {
-			st->pa->cause = p[2];
+			pc->para.cause = p[2];
 			if (p[1] > 1)
-				st->pa->loc = p[3];
+				pc->para.loc = p[3];
 			else
-				st->pa->loc = 0;
+				pc->para.loc = 0;
 		} else {
-			st->pa->cause = 0;
-			st->pa->loc = 0;
+			pc->para.cause = 0;
+			pc->para.loc = 0;
 		}
-	} else
-		st->pa->cause = -1;
-	SET_SKB_FREE(skb);
+	} else {
+		pc->para.cause = -1;
+		l3_1tr6_error(pc, "missing REL cause", skb);
+		return;
+	}
 	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);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	release_l3_process(pc);
 }
 
 static void
-l3_1tr6_rel_ack(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_rel_ack(struct l3_process *pc, 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);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->para.cause = -1;
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	release_l3_process(pc);
 }
 
 static void
-l3_1tr6_disc(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_disc(struct l3_process *pc, 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);
+	StopAllL3Timer(pc);
 	p = skb->data;
 	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
 		iecpy(a_charge, p, 1);
@@ -383,104 +444,110 @@
 			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);
+		if (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+		}
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
 		}
-	} else if (st->l3.debug & L3_DEB_CHARGE)
-		l3_debug(st, "charging info not found");
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->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];
+			pc->para.cause = p[2];
 			if (p[1] > 1)
-				st->pa->loc = p[3];
+				pc->para.loc = p[3];
 			else
-				st->pa->loc = 0;
+				pc->para.loc = 0;
 		} else {
-			st->pa->cause = 0;
-			st->pa->loc = 0;
+			pc->para.cause = 0;
+			pc->para.loc = 0;
 		}
 	} else {
-		if (st->l3.debug & L3_DEB_WARN)
-			l3_debug(st, "cause not found");
-		st->pa->cause = -1;
+		if (pc->st->l3.debug & L3_DEB_WARN)
+			l3_debug(pc->st, "cause not found");
+		pc->para.cause = -1;
+	}
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
 	}
-	SET_SKB_FREE(skb);
 	dev_kfree_skb(skb, FREE_READ);
-	newl3state(st, 12);
-	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+	newl3state(pc, 12);
+	pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
 }
 
 
 static void
-l3_1tr6_connect_ack(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb = arg;
 
-	SET_SKB_FREE(skb);
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
+	}
 	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);
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
 }
 
 static void
-l3_1tr6_alert_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg)
 {
-	newl3state(st, 7);
-	l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
+	newl3state(pc, 7);
+	l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1);
 }
 
 static void
-l3_1tr6_setup_rsp(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_setup_rsp(struct l3_process *pc, 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 ? */
+	MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1);
+	if (pc->para.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++ = pc->para.setup.si1;
+		*p++ = pc->para.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;
+		*p++ = pc->para.setup.si1;
+		*p++ = pc->para.setup.si2;
 	}
-	newl3state(st, 8);
+	newl3state(pc, 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);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
 }
 
 static void
-l3_1tr6_reset(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg)
 {
-	newl3state(st, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3_1tr6_disconnect_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -489,8 +556,8 @@
 	u_char cause = 0x10;
 	u_char clen = 1;
 
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 	/* Map DSS1 causes */
 	switch (cause & 0x7f) {
 		case 0x10:
@@ -500,57 +567,46 @@
 			cause = CAUSE_CallRejected;
 			break;
 	}
-	StopAllL3Timer(st);
-	MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1);
+	StopAllL3Timer(pc);
+	MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1);
 	*p++ = WE0_cause;
 	*p++ = clen;		/* Laenge */
 	if (clen)
 		*p++ = cause | 0x80;
-	newl3state(st, 11);
+	newl3state(pc, 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);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
 }
 
 static void
-l3_1tr6_release_req(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t303(struct l3_process *pc, 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);
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3_1tr6_setup_req(pc, pr, arg);
 	} else {
-		L3DelTimer(&st->l3.timer);
-		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
-		st->l3.n_t303 = 1;
-		newl3state(st, 0);
+		L3DelTimer(&pc->timer);
+		pc->para.cause = 0;
+		l3_1tr6_disconnect_req(pc, 0, NULL);
 	}
 }
 
 static void
-l3_1tr6_t304(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t304(struct l3_process *pc, 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);
-
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
-l3_1tr6_t305(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -559,9 +615,9 @@
 	u_char cause = 0x90;
 	u_char clen = 1;
 
-	L3DelTimer(&st->l3.timer);
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 	/* Map DSS1 causes */
 	switch (cause & 0x7f) {
 		case 0x10:
@@ -571,74 +627,74 @@
 			cause = CAUSE_CallRejected;
 			break;
 	}
-	MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1);
+	MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1);
 	*p++ = WE0_cause;
 	*p++ = clen;		/* Laenge */
 	if (clen)
 		*p++ = cause;
-	newl3state(st, 19);
+	newl3state(pc, 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);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
 static void
-l3_1tr6_t310(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t310(struct l3_process *pc, 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);
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 }
 
 static void
-l3_1tr6_t313(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t313(struct l3_process *pc, 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);
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
 }
 
 static void
-l3_1tr6_t308_1(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t308_1(struct l3_process *pc, 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);
+	L3DelTimer(&pc->timer);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+	newl3state(pc, 19);
 }
 
 static void
-l3_1tr6_t308_2(struct PStack *st, u_char pr, void *arg)
+l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
 {
-	L3DelTimer(&st->l3.timer);
-	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
-	newl3state(st, 0);
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	release_l3_process(pc);
 }
 /* *INDENT-OFF* */
 static struct stateentry downstl[] =
 {
 	{SBIT(0),
-	 CC_SETUP_REQ, l3_1tr6_setup_req},
+	 CC_SETUP | REQUEST, 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},
+    	 CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req},
 	{SBIT(12),
-	 CC_RELEASE_REQ, l3_1tr6_release_req},
+	 CC_RELEASE | REQUEST, l3_1tr6_release_req},
 	{ALL_STATES,
-	 CC_DLRL, l3_1tr6_reset},
+	 CC_DLRL | REQUEST, l3_1tr6_reset},
 	{SBIT(6),
-	 CC_IGNORE, l3_1tr6_reset},
+	 CC_IGNORE | REQUEST, l3_1tr6_reset},
 	{SBIT(6),
-	 CC_REJECT_REQ, l3_1tr6_disconnect_req},
+	 CC_REJECT | REQUEST, l3_1tr6_disconnect_req},
 	{SBIT(6),
-	 CC_ALERTING_REQ, l3_1tr6_alert_req},
+	 CC_ALERTING | REQUEST, l3_1tr6_alert_req},
 	{SBIT(6) | SBIT(7),
-	 CC_SETUP_RSP, l3_1tr6_setup_rsp},
+	 CC_SETUP | RESPONSE, l3_1tr6_setup_rsp},
 	{SBIT(1),
 	 CC_T303, l3_1tr6_t303},
 	{SBIT(2),
@@ -655,12 +711,14 @@
 	 CC_T308_2, l3_1tr6_t308_2},
 };
 
-static int downstl_len = sizeof(downstl) /
-sizeof(struct stateentry);
+#define DOWNSTL_LEN \
+	(sizeof(downstl) / sizeof(struct stateentry))
 
 static struct stateentry datastln1[] =
 {
 	{SBIT(0),
+	 MT_N1_INVALID, l3_1tr6_invalid},
+	{SBIT(0),
 	 MT_N1_SETUP, l3_1tr6_setup},
 	{SBIT(1),
 	 MT_N1_SETUP_ACK, l3_1tr6_setup_ack},
@@ -679,69 +737,132 @@
 	{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),
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
 	 MT_N1_REL, l3_1tr6_rel},
 	{SBIT(19),
+	 MT_N1_REL, l3_1tr6_rel_ack},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+	 MT_N1_REL_ACK, l3_1tr6_invalid},
+	{SBIT(19),
 	 MT_N1_REL_ACK, l3_1tr6_rel_ack}
 };
 /* *INDENT-ON* */
 
-
-
-static int datastln1_len = sizeof(datastln1) /
-sizeof(struct stateentry);
+#define DATASTLN1_LEN \
+	(sizeof(datastln1) / sizeof(struct stateentry))
 
 static void
 up1tr6(struct PStack *st, int pr, void *arg)
 {
-	int i, mt;
+	int i, mt, cr;
+	struct l3_process *proc;
 	struct sk_buff *skb = arg;
 	char tmp[80];
 
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+	}
+	if (skb->len < 4) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 len only %ld", skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb, FREE_READ);
+		return;
+	}
 	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);
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				skb->data[0], skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb, FREE_READ);
+		return;
+	}
+	if (skb->data[1] != 1) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 CR len not 1");
 			l3_debug(st, tmp);
 		}
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb, FREE_READ);
 		return;
 	}
-	mt = skb->data[skb->data[1] + 2];
+	cr = skb->data[2];
+	mt = skb->data[3];
 	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);
+			sprintf(tmp, "up1tr6%s N0 mt %x unhandled",
+			     (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt);
 			l3_debug(st, tmp);
 		}
 	} else if (skb->data[0] == PROTO_DIS_N1) {
-		for (i = 0; i < datastln1_len; i++)
+		if (!(proc = getl3proc(st, cr))) {
+			if (mt == MT_N1_SETUP) { 
+				if (cr < 128) {
+					if (!(proc = new_l3_process(st, cr))) {
+						if (st->l3.debug & L3_DEB_PROTERR) {
+							sprintf(tmp, "up1tr6 no roc mem");
+							l3_debug(st, tmp);
+						}
+						dev_kfree_skb(skb, FREE_READ);
+						return;
+					}
+				} else {
+					dev_kfree_skb(skb, FREE_READ);
+					return;
+				}
+			} else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) ||
+				(mt == MT_N1_CANC_ACK) || (mt == MT_N1_CANC_REJ) ||
+				(mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) ||
+				(mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) ||
+				(mt == MT_N1_INFO)) {
+				dev_kfree_skb(skb, FREE_READ);
+				return;
+			} else {
+				if (!(proc = new_l3_process(st, cr))) {
+					if (st->l3.debug & L3_DEB_PROTERR) {
+						sprintf(tmp, "up1tr6 no roc mem");
+						l3_debug(st, tmp);
+					}
+					dev_kfree_skb(skb, FREE_READ);
+					return;
+				}
+				mt = MT_N1_INVALID;
+			}
+		}
+		for (i = 0; i < DATASTLN1_LEN; i++)
 			if ((mt == datastln1[i].primitive) &&
-			    ((1 << st->l3.state) & datastln1[i].state))
+			    ((1 << proc->state) & datastln1[i].state))
 				break;
-		if (i == datastln1_len) {
-			SET_SKB_FREE(skb);
+		if (i == DATASTLN1_LEN) {
 			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);
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+					proc->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);
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+					proc->state, mt);
 				l3_debug(st, tmp);
 			}
-			datastln1[i].rout(st, pr, skb);
+			datastln1[i].rout(proc, pr, skb);
 		}
 	}
 }
@@ -749,26 +870,47 @@
 static void
 down1tr6(struct PStack *st, int pr, void *arg)
 {
-	int i;
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
 	char tmp[80];
 
-	for (i = 0; i < downstl_len; i++)
+	if (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if ((CC_SETUP | REQUEST) == pr) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if (!(proc = new_l3_process(st, cr))) {
+			return;
+		} else {
+			proc->chan = chan;
+			chan->proc = proc;
+			proc->para.setup = chan->setup;
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+
+	for (i = 0; i < DOWNSTL_LEN; i++)
 		if ((pr == downstl[i].primitive) &&
-		    ((1 << st->l3.state) & downstl[i].state))
+		    ((1 << proc->state) & downstl[i].state))
 			break;
-	if (i == downstl_len) {
+	if (i == DOWNSTL_LEN) {
 		if (st->l3.debug & L3_DEB_STATE) {
 			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
-				st->l3.state, pr);
+				proc->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);
+				proc->state, pr);
 			l3_debug(st, tmp);
 		}
-		downstl[i].rout(st, pr, arg);
+		downstl[i].rout(proc, pr, arg);
 	}
 }
 
@@ -777,20 +919,10 @@
 {
 	char tmp[64];
 
-	st->l4.l4l3 = down1tr6;
+	st->lli.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));
-	}
+	st->l3.N303 = 0;
+
+	strcpy(tmp, l3_1tr6_revision);
+	printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/l3_1tr6.h linux/drivers/isdn/hisax/l3_1tr6.h
--- v2.0.35/linux/drivers/isdn/hisax/l3_1tr6.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/l3_1tr6.h	Sun Nov 15 10:32:58 1998
@@ -1,12 +1,19 @@
-/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $
+/* $Id: l3_1tr6.h,v 1.1.2.2 1998/09/27 13:06:46 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
+ * Revision 1.1.2.2  1998/09/27 13:06:46  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
+ * Revision 1.1.2.1  1997/10/17 22:14:15  keil
+ * update to last hisax version
  *
+ * Revision 2.0  1997/07/27 21:15:47  keil
+ * New Callref based layer3
+ *
+ * Revision 1.1  1996/10/13 20:03:48  keil
+ * Initial revision
  *
  */
 #ifndef l3_1tr6
@@ -29,7 +36,6 @@
 #define MT_N0_CLOSE   0x75
 #define MT_N0_CLO_ACK 0x77
 
-
 /*
  * MsgType N1
  */
@@ -64,8 +70,7 @@
 #define MT_N1_REG_ACK 0x6C
 #define MT_N1_REG_REJ 0x6F
 #define MT_N1_STAT 0x63
-
-
+#define MT_N1_INVALID 0
 
 /*
  * W Elemente
@@ -156,5 +161,13 @@
 #define CAUSE_RemoteUserResumed 0x73
 #define CAUSE_UserInfoDiscarded 0x7F
 
+#define T303	4000
+#define T304	20000
+#define T305	4000
+#define T308	4000
+#define T310	120000
+#define T313	4000
+#define T318	4000
+#define T319	4000
 
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.0.35/linux/drivers/isdn/hisax/l3dss1.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/l3dss1.c	Sun Nov 15 10:32:58 1998
@@ -1,82 +1,303 @@
-/* $Id: l3dss1.c,v 1.16 1997/06/03 20:43:46 keil Exp $
+/* $Id: l3dss1.c,v 1.16.2.8 1998/11/03 00:07:14 keil Exp $
 
  * EURO/DSS1 D-channel protocol
  *
  * Author       Karsten Keil (keil@temic-ech.spacenet.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * 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.16.2.8  1998/11/03 00:07:14  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.11  1997/02/16 12:12:47  fritz
- * Bugfix: SI2 was nont initialized on incoming calls.
+ * Revision 1.16.2.7  1998/10/25 18:16:25  fritz
+ * Replaced some read-only variables by defines.
  *
- * Revision 1.10  1997/02/11 01:37:24  keil
- * Changed setup-interface (incoming and outgoing)
+ * Revision 1.16.2.6  1998/10/23 15:00:56  fritz
+ * Eliminated a compiler warning.
  *
- * Revision 1.9  1997/01/27 23:20:52  keil
- * report revision only ones
+ * Revision 1.16.2.5  1998/09/27 13:06:48  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
  *
- * Revision 1.8  1997/01/21 22:29:41  keil
- * new statemachine; L3 timers
+ * Revision 1.16.2.4  1998/05/27 18:06:08  keil
+ * HiSax 3.0
  *
- * Revision 1.7  1996/12/14 21:06:59  keil
- * additional states for CC_REJECT
+ * Revision 1.16.2.3  1998/02/03 23:16:06  keil
+ * german AOC
  *
- * Revision 1.6  1996/12/08 22:59:16  keil
- * fixed calling party number without octet 3a
+ * Revision 1.16.2.2  1997/11/15 18:54:15  keil
+ * cosmetics
  *
- * Revision 1.5  1996/12/08 19:53:31  keil
- * fixes from Pekka Sarnila
+ * Revision 1.16.2.1  1997/10/17 22:14:16  keil
+ * update to last hisax version
  *
- * Revision 1.4  1996/11/05 19:44:36  keil
- * some fixes from Henner Eisen
+ * Revision 2.2  1997/08/07 17:44:36  keil
+ * Fix RESTART
  *
- * Revision 1.3  1996/10/30 10:18:01  keil
- * bugfixes in debugging output
+ * Revision 2.1  1997/08/03 14:36:33  keil
+ * Implement RESTART procedure
  *
- * Revision 1.2  1996/10/27 22:15:16  keil
- * bugfix reject handling
+ * Revision 2.0  1997/07/27 21:15:43  keil
+ * New Callref based layer3
  *
- * Revision 1.1  1996/10/13 20:04:55  keil
- * Initial revision
+ * Revision 1.17  1997/06/26 11:11:46  keil
+ * SET_SKBFREE now on creation of a SKB
  *
+ * Revision 1.15  1997/04/17 11:50:48  keil
+ * pa->loc was undefined, if it was not send by the exchange
  *
+ * Old log removed /KKe
  *
  */
 
 #define __NO_VERSION__
 #include "hisax.h"
 #include "isdnl3.h"
+#include "l3dss1.h"
 #include <linux/ctype.h>
 
 extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 1.16 $";
+const char *dss1_revision = "$Revision: 1.16.2.8 $";
+
+#define EXT_BEARER_CAPS 1
 
 #define	MsgHead(ptr, cref, mty) \
 	*ptr++ = 0x8; \
 	*ptr++ = 0x1; \
-	*ptr++ = cref; \
+	*ptr++ = cref^0x80; \
 	*ptr++ = mty
 
+
+#if HISAX_DE_AOC
 static void
-l3dss1_message(struct PStack *st, u_char mt)
+l3dss1_parse_facility(struct l3_process *pc, u_char * p)
+{
+	int qd_len = 0;
+
+	p++;
+	qd_len = *p++;
+	if (qd_len == 0) {
+		l3_debug(pc->st, "qd_len == 0");
+		return;
+	}
+	if ((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+		l3_debug(pc->st, "supplementary service != 0x11");
+		return;
+	}
+	while (qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++;
+		qd_len--;
+	}
+	if (qd_len < 2) {
+		l3_debug(pc->st, "qd_len < 2");
+		return;
+	}
+	p++;
+	qd_len--;
+	if ((*p & 0xE0) != 0xA0) {	/* class and form */
+		l3_debug(pc->st, "class and form != 0xA0");
+		return;
+	}
+	switch (*p & 0x1F) {	/* component tag */
+		case 1:	/* invoke */
+			{
+				unsigned char nlen = 0, ilen;
+				int ident;
+
+				p++;
+				qd_len--;
+				if (qd_len < 1) {
+					l3_debug(pc->st, "qd_len < 1");
+					break;
+				}
+				if (*p & 0x80) {	/* length format */
+					l3_debug(pc->st, "*p & 0x80 length format");
+					break;
+				}
+				nlen = *p++;
+				qd_len--;
+				if (qd_len < nlen) {
+					l3_debug(pc->st, "qd_len < nlen");
+					return;
+				}
+				qd_len -= nlen;
+
+				if (nlen < 2) {
+					l3_debug(pc->st, "nlen < 2");
+					return;
+				}
+				if (*p != 0x02) {	/* invoke identifier tag */
+					l3_debug(pc->st, "invoke identifier tag !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				if (*p & 0x80) {	/* length format */
+					l3_debug(pc->st, "*p & 0x80 length format 2");
+					break;
+				}
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(pc->st, "ilen > nlen || ilen == 0");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);	/* invoke identifier */
+					ilen--;
+				}
+
+				if (nlen < 2) {
+					l3_debug(pc->st, "nlen < 2 22");
+					return;
+				}
+				if (*p != 0x02) {	/* operation value */
+					l3_debug(pc->st, "operation value !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(pc->st, "ilen > nlen || ilen == 0 22");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);
+					ilen--;
+				}
+
+#define FOO1(s,a,b) \
+	    while(nlen > 1) {		\
+		    int ilen = p[1];	\
+		    if(nlen < ilen+2) {	\
+			    l3_debug(pc->st, "FOO1  nlen < ilen+2"); \
+			    return;		\
+		    }			\
+		    nlen -= ilen+2;		\
+		    if((*p & 0xFF) == (a)) {	\
+			    int nlen = ilen;	\
+			    p += 2;		\
+			    b;		\
+		    } else {		\
+			    p += ilen+2;	\
+		    }			\
+	    }
+
+				switch (ident) {
+					default:
+						break;
+					case 0x22:	/* during */
+						FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+														     ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+														     if (ident > pc->para.chargeinfo) {
+														     pc->para.chargeinfo = ident;
+														     pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+									}
+														     if (pc->st->l3.debug & L3_DEB_CHARGE) {
+														     if (*(p + 2) == 0) {
+														     l3_debug(pc->st, "charging info during %d", pc->para.chargeinfo);
+									}
+								   else {
+														     l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+									}
+								    )))))
+							break;
+					case 0x24:	/* final */
+						FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+																      ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+																      if (ident > pc->para.chargeinfo) {
+																      pc->para.chargeinfo = ident;
+																      pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+									}
+																      if (pc->st->l3.debug & L3_DEB_CHARGE) {
+																      l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+								   ))))))
+							break;
+				}
+#undef FOO1
+
+			}
+			break;
+		case 2:	/* return result */
+			l3_debug(pc->st, "return result break");
+			break;
+		case 3:	/* return error */
+			l3_debug(pc->st, "return error break");
+			break;
+		default:
+			l3_debug(pc->st, "default break");
+			break;
+	}
+}
+#endif
+
+static int
+l3dss1_check_messagetype_validity(int mt)
+{
+/* verify if a message type exists */
+	switch (mt) {
+		case MT_ALERTING:
+		case MT_CALL_PROCEEDING:
+		case MT_CONNECT:
+		case MT_CONNECT_ACKNOWLEDGE:
+		case MT_PROGRESS:
+		case MT_SETUP:
+		case MT_SETUP_ACKNOWLEDGE:
+		case MT_RESUME:
+		case MT_RESUME_ACKNOWLEDGE:
+		case MT_RESUME_REJECT:
+		case MT_SUSPEND:
+		case MT_SUSPEND_ACKNOWLEDGE:
+		case MT_SUSPEND_REJECT:
+		case MT_USER_INFORMATION:
+		case MT_DISCONNECT:
+		case MT_RELEASE:
+		case MT_RELEASE_COMPLETE:
+		case MT_RESTART:
+		case MT_RESTART_ACKNOWLEDGE:
+		case MT_SEGMENT:
+		case MT_CONGESTION_CONTROL:
+		case MT_INFORMATION:
+		case MT_FACILITY:
+		case MT_NOTIFY:
+		case MT_STATUS:
+		case MT_STATUS_ENQUIRY:
+			return(1);
+		default:
+			return(0);
+	}
+	return(0);
+}
+
+static void
+l3dss1_message(struct l3_process *pc, u_char mt)
 {
 	struct sk_buff *skb;
 	u_char *p;
@@ -84,44 +305,283 @@
 	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);
+	MsgHead(p, pc->callref, mt);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
-l3dss1_release_req(struct PStack *st, u_char pr, void *arg)
+l3dss1_release_req(struct l3_process *pc, 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);
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
 }
 
 static void
-l3dss1_release_cmpl(struct PStack *st, u_char pr, void *arg)
+l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 	int cause = -1;
 
 	p = skb->data;
-	st->pa->loc = 0;
+	pc->para.loc = 0;
 	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
 		p++;
 		if (*p++ == 2)
-			st->pa->loc = *p++;
+			pc->para.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);
+	StopAllL3Timer(pc);
+	pc->para.cause = cause;
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	release_l3_process(pc);
+}
+
+#if EXT_BEARER_CAPS
+
+u_char *
+EncodeASyncParams(u_char * p, u_char si2)
+{				// 7c 06 88  90 21 42 00 bb
+
+	p[0] = p[1] = 0;
+	p[2] = 0x80;
+	if (si2 & 32)		// 7 data bits
+
+		p[2] += 16;
+	else			// 8 data bits
+
+		p[2] += 24;
+
+	if (si2 & 16)		// 2 stop bits
+
+		p[2] += 96;
+	else			// 1 stop bit
+
+		p[2] = 32;
+
+	if (si2 & 8)		// even parity
+
+		p[2] += 2;
+	else			// no parity
+
+		p[2] += 3;
+
+	switch (si2 & 0x07) {
+		case 0:
+			p[0] = 66;	// 1200 bit/s
+
+			break;
+		case 1:
+			p[0] = 88;	// 1200/75 bit/s
+
+			break;
+		case 2:
+			p[0] = 87;	// 75/1200 bit/s
+
+			break;
+		case 3:
+			p[0] = 67;	// 2400 bit/s
+
+			break;
+		case 4:
+			p[0] = 69;	// 4800 bit/s
+
+			break;
+		case 5:
+			p[0] = 72;	// 9600 bit/s
+
+			break;
+		case 6:
+			p[0] = 73;	// 14400 bit/s
+
+			break;
+		case 7:
+			p[0] = 75;	// 19200 bit/s
+
+			break;
+	}
+	return p + 3;
+}
+
+u_char
+EncodeSyncParams(u_char si2, u_char ai)
+{
+
+	switch (si2) {
+		case 0:
+			return ai + 2;	// 1200 bit/s
+
+		case 1:
+			return ai + 24;		// 1200/75 bit/s
+
+		case 2:
+			return ai + 23;		// 75/1200 bit/s
+
+		case 3:
+			return ai + 3;	// 2400 bit/s
+
+		case 4:
+			return ai + 5;	// 4800 bit/s
+
+		case 5:
+			return ai + 8;	// 9600 bit/s
+
+		case 6:
+			return ai + 9;	// 14400 bit/s
+
+		case 7:
+			return ai + 11;		// 19200 bit/s
+
+		case 8:
+			return ai + 14;		// 48000 bit/s
+
+		case 9:
+			return ai + 15;		// 56000 bit/s
+
+		case 15:
+			return ai + 40;		// negotiate bit/s
+
+		default:
+			break;
+	}
+	return ai;
+}
+
+
+static u_char
+DecodeASyncParams(u_char si2, u_char * p)
+{
+	u_char info;
+
+	switch (p[5]) {
+		case 66:	// 1200 bit/s
+
+			break;	// si2 don't change
+
+		case 88:	// 1200/75 bit/s
+
+			si2 += 1;
+			break;
+		case 87:	// 75/1200 bit/s
+
+			si2 += 2;
+			break;
+		case 67:	// 2400 bit/s
+
+			si2 += 3;
+			break;
+		case 69:	// 4800 bit/s
+
+			si2 += 4;
+			break;
+		case 72:	// 9600 bit/s
+
+			si2 += 5;
+			break;
+		case 73:	// 14400 bit/s
+
+			si2 += 6;
+			break;
+		case 75:	// 19200 bit/s
+
+			si2 += 7;
+			break;
+	}
+
+	info = p[7] & 0x7f;
+	if ((info & 16) && (!(info & 8)))	// 7 data bits
+
+		si2 += 32;	// else 8 data bits
+
+	if ((info & 96) == 96)	// 2 stop bits
+
+		si2 += 16;	// else 1 stop bit
+
+	if ((info & 2) && (!(info & 1)))	// even parity
+
+		si2 += 8;	// else no parity
+
+	return si2;
 }
 
+
+static u_char
+DecodeSyncParams(u_char si2, u_char info)
+{
+	info &= 0x7f;
+	switch (info) {
+		case 40:	// bit/s negotiation failed  ai := 165 not 175!
+
+			return si2 + 15;
+		case 15:	// 56000 bit/s failed, ai := 0 not 169 !
+
+			return si2 + 9;
+		case 14:	// 48000 bit/s
+
+			return si2 + 8;
+		case 11:	// 19200 bit/s
+
+			return si2 + 7;
+		case 9:	// 14400 bit/s
+
+			return si2 + 6;
+		case 8:	// 9600  bit/s
+
+			return si2 + 5;
+		case 5:	// 4800  bit/s
+
+			return si2 + 4;
+		case 3:	// 2400  bit/s
+
+			return si2 + 3;
+		case 23:	// 75/1200 bit/s
+
+			return si2 + 2;
+		case 24:	// 1200/75 bit/s
+
+			return si2 + 1;
+		default:	// 1200 bit/s
+
+			return si2;
+	}
+}
+
+static u_char
+DecodeSI2(struct sk_buff *skb)
+{
+	u_char *p;		//, *pend=skb->data + skb->len;
+
+	if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+		switch (p[4] & 0x0f) {
+			case 0x01:
+				if (p[1] == 0x04)	// sync. Bitratenadaption
+
+					return DecodeSyncParams(160, p[5]);	// V.110/X.30
+
+				else if (p[1] == 0x06)	// async. Bitratenadaption
+
+					return DecodeASyncParams(192, p);	// V.110/X.30
+
+				break;
+			case 0x08:	// if (p[5] == 0x02) // sync. Bitratenadaption
+
+				return DecodeSyncParams(176, p[5]);	// V.120
+
+				break;
+		}
+	}
+	return 0;
+}
+
+#endif
+
+
 static void
-l3dss1_setup_req(struct PStack *st, u_char pr,
+l3dss1_setup_req(struct l3_process *pc, u_char pr,
 		 void *arg)
 {
 	struct sk_buff *skb;
@@ -131,16 +591,19 @@
 	u_char screen = 0x80;
 	u_char *teln;
 	u_char *msn;
+	u_char *sub;
+	u_char *sp;
 	int l;
 
-	st->l3.callref = st->pa->callref;
-	MsgHead(p, st->l3.callref, MT_SETUP);
+	MsgHead(p, pc->callref, MT_SETUP);
 
 	/*
 	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
 	 */
+#if HISAX_EURO_SENDCOMPLETE
 	*p++ = 0xa1;		/* complete indicator */
-	switch (st->pa->setup.si1) {
+#endif
+	switch (pc->para.setup.si1) {
 		case 1:	/* Telephony                               */
 			*p++ = 0x4;	/* BC-IE-code                              */
 			*p++ = 0x3;	/* Length                                  */
@@ -160,7 +623,7 @@
 	/*
 	 * What about info2? Mapping to High-Layer-Compatibility?
 	 */
-	teln = st->pa->setup.phone;
+	teln = pc->para.setup.phone;
 	if (*teln) {
 		/* parse number for special things */
 		if (!isdigit(*teln)) {
@@ -182,19 +645,28 @@
 					screen = 0x80;
 					break;
 				default:
-					if (st->l3.debug & L3_DEB_WARN)
-						l3_debug(st, "Wrong MSN Code");
+					if (pc->debug & L3_DEB_WARN)
+						l3_debug(pc->st, "Wrong MSN Code");
 					break;
 			}
 			teln++;
 		}
 	}
 	if (channel) {
-		*p++ = 0x18;	/* channel indicator */
+		*p++ = IE_CHANNEL_ID;
 		*p++ = 1;
 		*p++ = channel;
 	}
-	msn = st->pa->setup.eazmsn;
+	msn = pc->para.setup.eazmsn;
+	sub = NULL;
+	sp = msn;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
 	if (*msn) {
 		*p++ = 0x6c;
 		*p++ = strlen(msn) + (screen ? 2 : 1);
@@ -207,241 +679,389 @@
 		while (*msn)
 			*p++ = *msn++ & 0x7f;
 	}
+	if (sub) {
+		*sub++ = '.';
+		*p++ = 0x6d;	/* Calling party subaddress */
+		*p++ = strlen(sub) + 2;
+		*p++ = 0x80;	/* NSAP coded */
+		*p++ = 0x50;	/* local IDI format */
+		while (*sub)
+			*p++ = *sub++ & 0x7f;
+	}
+	sub = NULL;
+	sp = teln;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
 	*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;
 
+	if (sub) {
+		*sub++ = '.';
+		*p++ = 0x71;	/* Called party subaddress */
+		*p++ = strlen(sub) + 2;
+		*p++ = 0x80;	/* NSAP coded */
+		*p++ = 0x50;	/* local IDI format */
+		while (*sub)
+			*p++ = *sub++ & 0x7f;
+	}
+#if EXT_BEARER_CAPS
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
+
+		*p++ = 0x7c;
+		*p++ = 0x04;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
+
+		*p++ = 0x7c;
+		*p++ = 0x05;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x28;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+		*p++ = 0x82;
+	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
+
+		*p++ = 0x7c;
+		*p++ = 0x06;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+#if HISAX_SEND_STD_LLC_IE
+	} else {
+		*p++ = 0x7c;
+		*p++ = 0x02;
+		*p++ = 0x88;
+		*p++ = 0x90;
+#endif
+	}
+#endif
 	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);
-
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
-l3dss1_call_proc(struct PStack *st, u_char pr, void *arg)
+l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->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);
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN))
+			l3_debug(pc->st, "setup answer without bchannel");
+	} else if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup answer without bchannel");
 	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);
+	newl3state(pc, 3);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
 }
 
 static void
-l3dss1_setup_ack(struct PStack *st, u_char pr, void *arg)
+l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 
-	L3DelTimer(&st->l3.timer);
+	L3DelTimer(&pc->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);
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN))
+			l3_debug(pc->st, "setup answer without bchannel");
+	} else if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "setup answer without bchannel");
 	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);
+	newl3state(pc, 2);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
 }
 
 static void
-l3dss1_disconnect(struct PStack *st, u_char pr, void *arg)
+l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
 	int cause = -1;
 
-	StopAllL3Timer(st);
+	StopAllL3Timer(pc);
 	p = skb->data;
-	st->pa->loc = 0;
+	pc->para.loc = 0;
 	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
 		p++;
 		if (*p++ == 2)
-			st->pa->loc = *p++;
+			pc->para.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);
+	newl3state(pc, 12);
+	pc->para.cause = cause;
+	pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
 }
 
 static void
-l3dss1_connect(struct PStack *st, u_char pr, void *arg)
+l3dss1_connect(struct l3_process *pc, 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);
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
 }
 
 static void
-l3dss1_alerting(struct PStack *st, u_char pr, void *arg)
+l3dss1_alerting(struct l3_process *pc, 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);
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
 }
 
 static void
-l3dss1_setup(struct PStack *st, u_char pr, void *arg)
+l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
 {
-	u_char *p;
+	/* This routine is called if here was no SETUP made (checks in dss1up and in
+	 * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+	 * MT_STATUS_ENQUIRE in the NULL state is handled too
+	 */
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	switch (pc->para.cause) {
+		case 81:	/* 0x51 invalid callreference */
+		case 88:	/* 0x58 incomp destination */
+		case 96:	/* 0x60 mandory IE missing */
+		case 101:	/* 0x65 incompatible Callstate */
+			MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+			*p++ = IE_CAUSE;
+			*p++ = 0x2;
+			*p++ = 0x80;
+			*p++ = pc->para.cause | 0x80;
+			break;
+		default:
+			printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n");
+			return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	release_l3_process(pc);
+}
+
+static void
+l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p, *ptmp[8];
+	int i;
 	int bcfound = 0;
 	char tmp[80];
 	struct sk_buff *skb = arg;
 
+	/* ETS 300-104 1.3.4 and 1.3.5
+	 * we need to detect unknown inform. element from 0 to 7
+	 */
 	p = skb->data;
-	st->pa->callref = getcallref(p);
-	st->l3.callref = 0x80 + st->pa->callref;
-
+	for (i = 0; i < 8; i++)
+		ptmp[i] = skb->data;
+	if (findie(ptmp[1], skb->len, 0x01, 0)
+	    || findie(ptmp[2], skb->len, 0x02, 0)
+	    || findie(ptmp[3], skb->len, 0x03, 0)
+	    || findie(ptmp[5], skb->len, 0x05, 0)
+	    || findie(ptmp[6], skb->len, 0x06, 0)
+	    || findie(ptmp[7], skb->len, 0x07, 0)) {
+		/* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE
+		 * cause 0x60
+		 */
+		pc->para.cause = 0x60;
+		dev_kfree_skb(skb, FREE_READ);
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
 	/*
 	 * Channel Identification
 	 */
 	p = skb->data;
-	if ((p = findie(p, skb->len, 0x18, 0))) {
-		st->pa->bchannel = p[2] & 0x3;
-		if (st->pa->bchannel)
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if (pc->para.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");
-
+		else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bchannel");
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bchannel");
+		pc->para.cause = 0x60;
+		dev_kfree_skb(skb, FREE_READ);
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
 	/*
 	   * Bearer Capabilities
 	 */
 	p = skb->data;
 	if ((p = findie(p, skb->len, 0x04, 0))) {
-		st->pa->setup.si2 = 0;
+		pc->para.setup.si2 = 0;
 		switch (p[2] & 0x1f) {
 			case 0x00:
 				/* Speech */
 			case 0x10:
 				/* 3.1 Khz audio */
-				st->pa->setup.si1 = 1;
+				pc->para.setup.si1 = 1;
 				break;
 			case 0x08:
 				/* Unrestricted digital information */
-				st->pa->setup.si1 = 7;
+				pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#if EXT_BEARER_CAPS
+				pc->para.setup.si2 = DecodeSI2(skb);
+				printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n",
+				 pc->para.setup.si1, pc->para.setup.si2);
+#endif
 				break;
 			case 0x09:
 				/* Restricted digital information */
-				st->pa->setup.si1 = 2;
+				pc->para.setup.si1 = 2;
 				break;
 			case 0x11:
 				/* Unrestr. digital information  with tones/announcements */
-				st->pa->setup.si1 = 3;
+				pc->para.setup.si1 = 3;
 				break;
 			case 0x18:
 				/* Video */
-				st->pa->setup.si1 = 4;
+				pc->para.setup.si1 = 4;
 				break;
 			default:
-				st->pa->setup.si1 = 0;
+				pc->para.setup.si1 = 0;
 		}
-	} else if (st->l3.debug & L3_DEB_WARN)
-		l3_debug(st, "setup without bearer capabilities");
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bearer capabilities");
+		/* ETS 300-104 1.3.3 */
+		pc->para.cause = 0x60;
+		dev_kfree_skb(skb, FREE_READ);
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
 
 	p = skb->data;
 	if ((p = findie(p, skb->len, 0x70, 0)))
-		iecpy(st->pa->setup.eazmsn, p, 1);
+		iecpy(pc->para.setup.eazmsn, p, 1);
 	else
-		st->pa->setup.eazmsn[0] = 0;
+		pc->para.setup.eazmsn[0] = 0;
 
 	p = skb->data;
+	if ((p = findie(p, skb->len, 0x71, 0))) {
+		/* Called party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.eazmsn, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong called subaddress");
+	}
+	p = skb->data;
 	if ((p = findie(p, skb->len, 0x6c, 0))) {
-		st->pa->setup.plan = p[2];
+		pc->para.setup.plan = p[2];
 		if (p[2] & 0x80) {
-			iecpy(st->pa->setup.phone, p, 1);
-			st->pa->setup.screen = 0;
+			iecpy(pc->para.setup.phone, p, 1);
+			pc->para.setup.screen = 0;
 		} else {
-			iecpy(st->pa->setup.phone, p, 2);
-			st->pa->setup.screen = p[3];
+			iecpy(pc->para.setup.phone, p, 2);
+			pc->para.setup.screen = p[3];
 		}
 	} else {
-		st->pa->setup.phone[0] = 0;
-		st->pa->setup.plan = 0;
-		st->pa->setup.screen = 0;
+		pc->para.setup.phone[0] = 0;
+		pc->para.setup.plan = 0;
+		pc->para.setup.screen = 0;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6d, 0))) {
+		/* Calling party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.phone, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong calling subaddress");
 	}
-	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);
+		if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) {
+			l3_debug(pc->st, "non-digital call: %s -> %s",
+			    pc->para.setup.phone, pc->para.setup.eazmsn);
 		}
-		newl3state(st, 6);
-		st->l3.l3l4(st, CC_SETUP_IND, NULL);
-	}
+		if ((pc->para.setup.si1 != 7) &&
+		    test_bit(FLG_PTP, &pc->st->l2.flag)) {
+			pc->para.cause = 0x58;
+			l3dss1_msg_without_setup(pc, pr, NULL);
+			return;
+		}
+		newl3state(pc, 6);
+		pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+	} else
+		release_l3_process(pc);
 }
 
 static void
-l3dss1_reset(struct PStack *st, u_char pr, void *arg)
+l3dss1_reset(struct l3_process *pc, u_char pr, void *arg)
 {
-	StopAllL3Timer(st);
-	newl3state(st, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3dss1_setup_rsp(struct PStack *st, u_char pr,
+l3dss1_setup_rsp(struct l3_process *pc, 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);
+	newl3state(pc, 8);
+	l3dss1_message(pc, MT_CONNECT);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
 }
 
 static void
-l3dss1_connect_ack(struct PStack *st, u_char pr, void *arg)
+l3dss1_connect_ack(struct l3_process *pc, 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);
+	newl3state(pc, 10);
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
 }
 
 static void
-l3dss1_disconnect_req(struct PStack *st, u_char pr, void *arg)
+l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -449,12 +1069,12 @@
 	int l;
 	u_char cause = 0x10;
 
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 
-	StopAllL3Timer(st);
+	StopAllL3Timer(pc);
 
-	MsgHead(p, st->l3.callref, MT_DISCONNECT);
+	MsgHead(p, pc->callref, MT_DISCONNECT);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -465,13 +1085,13 @@
 	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);
+	newl3state(pc, 11);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
 }
 
 static void
-l3dss1_reject_req(struct PStack *st, u_char pr, void *arg)
+l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg)
 {
 	struct sk_buff *skb;
 	u_char tmp[16];
@@ -479,10 +1099,10 @@
 	int l;
 	u_char cause = 0x95;
 
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
 
-	MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE);
+	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -493,13 +1113,14 @@
 	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);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3dss1_release(struct PStack *st, u_char pr, void *arg)
+l3dss1_release(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char *p;
 	struct sk_buff *skb = arg;
@@ -509,38 +1130,45 @@
 	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
 		p++;
 		if (*p++ == 2)
-			st->pa->loc = *p++;
+			pc->para.loc = *p++;
 		cause = *p & 0x7f;
 	}
-	SET_SKB_FREE(skb);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
+#if HISAX_DE_AOC
+		l3dss1_parse_facility(pc, p);
+#else
+		p = NULL;
+#endif
+	}
 	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);
+	StopAllL3Timer(pc);
+	pc->para.cause = cause;
+	l3dss1_message(pc, MT_RELEASE_COMPLETE);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	release_l3_process(pc);
 }
 
 static void
-l3dss1_alert_req(struct PStack *st, u_char pr,
+l3dss1_alert_req(struct l3_process *pc, u_char pr,
 		 void *arg)
 {
-	newl3state(st, 7);
-	l3dss1_message(st, MT_ALERTING);
+	newl3state(pc, 7);
+	l3dss1_message(pc, MT_ALERTING);
 }
 
 static void
-l3dss1_status_enq(struct PStack *st, u_char pr, void *arg)
+l3dss1_status_enq(struct l3_process *pc, 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);
+	MsgHead(p, pc->callref, MT_STATUS);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -549,42 +1177,96 @@
 
 	*p++ = 0x14;		/* CallState */
 	*p++ = 0x1;
-	*p++ = st->l3.state & 0x3f;
+	*p++ = pc->state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	/* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1...
+	   if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62  */
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb, FREE_READ);
+
+	MsgHead(p, pc->callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = 0x62 | 0x80;	/* status sending */
+
+	*p++ = 0x14;		/* CallState */
+	*p++ = 0x1;
+	*p++ = pc->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);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 
 static void
-l3dss1_t303(struct PStack *st, u_char pr, void *arg)
+l3dss1_release_ind(struct l3_process *pc, 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);
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int callState = 0;
+	p = skb->data;
+
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++)
+			callState = *p;
+	}
+	if (callState == 0) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
+		 * set down layer 3 without sending any message
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		release_l3_process(pc);
 	} else {
-		newl3state(st, 0);
-		L3DelTimer(&st->l3.timer);
-		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
-		st->l3.n_t303 = 1;
+		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
 	}
 }
 
 static void
-l3dss1_t304(struct PStack *st, u_char pr, void *arg)
+l3dss1_t303(struct l3_process *pc, 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);
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3dss1_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		release_l3_process(pc);
+	}
+}
+
+static void
+l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
 
 }
 
 static void
-l3dss1_t305(struct PStack *st, u_char pr, void *arg)
+l3dss1_t305(struct l3_process *pc, u_char pr, void *arg)
 {
 	u_char tmp[16];
 	u_char *p = tmp;
@@ -592,11 +1274,11 @@
 	struct sk_buff *skb;
 	u_char cause = 0x90;
 
-	L3DelTimer(&st->l3.timer);
-	if (st->pa->cause > 0)
-		cause = st->pa->cause;
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause > 0)
+		cause = pc->para.cause | 0x80;
 
-	MsgHead(p, st->l3.callref, MT_RELEASE);
+	MsgHead(p, pc->callref, MT_RELEASE);
 
 	*p++ = IE_CAUSE;
 	*p++ = 0x2;
@@ -607,64 +1289,352 @@
 	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);
+	newl3state(pc, 19);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+	newl3state(pc, 19);
+	L3DelTimer(&pc->timer);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+}
+
+static void
+l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0x66;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 19);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0x66;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_DLRL | INDICATION, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3dss1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	char tmp[64], *t;
+	int l;
+	struct sk_buff *skb = arg;
+	int cause, callState;
+
+	cause = callState = -1;
+	p = skb->data;
+	t = tmp;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		l = *p++;
+		t += sprintf(t, "Status CR %x Cause:", pc->callref);
+		while (l--) {
+			cause = *p;
+			t += sprintf(t, " %2x", *p++);
+		}
+	} else
+		sprintf(t, "Status CR %x no Cause", pc->callref);
+	l3_debug(pc->st, tmp);
+	p = skb->data;
+	t = tmp;
+	t += sprintf(t, "Status state %x ", pc->state);
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++) {
+			callState = *p;
+			t += sprintf(t, "peer state %x", *p);
+		} else
+			t += sprintf(t, "peer state len error");
+	} else
+		sprintf(t, "no peer state");
+	l3_debug(pc->st, tmp);
+	if (((cause & 0x7f) == 0x6f) && (callState == 0)) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
+		 * if received MT_STATUS with cause == 0x6f and call
+		 * state == 0, then we must set down layer 3
+		 */
+		l3dss1_release_ind(pc, pr, arg);
+	} else
+		dev_kfree_skb(skb, FREE_READ);
+}
+
+static void
+l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
+#if HISAX_DE_AOC
+		l3dss1_parse_facility(pc, p);
+#else
+		p = NULL;
+#endif
+	}
+}
+
+static void
+l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->chan->setup.phone;
+
+	MsgHead(p, pc->callref, MT_SUSPEND);
+
+	*p++ = IE_CALLID;
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else {
+		l3_debug(pc->st, "SUS wrong CALLID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 15);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 0);
+	dev_kfree_skb(skb, FREE_READ);
+	pc->para.cause = -1;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int cause = -1;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			pc->para.loc = *p++;
+		cause = *p & 0x7f;
+	}
+	dev_kfree_skb(skb, FREE_READ);
+	pc->para.cause = cause;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
 }
 
 static void
-l3dss1_t310(struct PStack *st, u_char pr, void *arg)
+l3dss1_resume_req(struct l3_process *pc, 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);
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->para.setup.phone;
+
+	MsgHead(p, pc->callref, MT_RESUME);
+
+	*p++ = IE_CALLID;
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else {
+		l3_debug(pc->st, "RES wrong CALLID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 17);
+	L3AddTimer(&pc->timer, T319, CC_T319);
 }
 
 static void
-l3dss1_t313(struct PStack *st, u_char pr, void *arg)
+l3dss1_resume_ack(struct l3_process *pc, 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);
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		pc->para.bchannel = p[2] & 0x3;
+		if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN))
+			l3_debug(pc->st, "resume ack without bchannel");
+	} else if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "resume ack without bchannel");
+	dev_kfree_skb(skb, FREE_READ);
+	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	newl3state(pc, 10);
 }
 
 static void
-l3dss1_t308_1(struct PStack *st, u_char pr, void *arg)
+l3dss1_resume_rej(struct l3_process *pc, 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);
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int cause = -1;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			pc->para.loc = *p++;
+		cause = *p & 0x7f;
+	}
+	dev_kfree_skb(skb, FREE_READ);
+	pc->para.cause = cause;
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	release_l3_process(pc);
 }
 
 static void
-l3dss1_t308_2(struct PStack *st, u_char pr, void *arg)
+l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
 {
-	newl3state(st, 0);
-	L3DelTimer(&st->l3.timer);
-	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
+	u_char tmp[32];
+	u_char *p;
+	u_char ri, ch = 0, chan = 0;
+	int l;
+	struct sk_buff *skb = arg;
+	struct l3_process *up;
+
+	newl3state(pc, 2);
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+		ri = p[2];
+		l3_debug(pc->st, "Restart %x", ri);
+	} else {
+		l3_debug(pc->st, "Restart without restart IE");
+		ri = 0x86;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		chan = p[2] & 3;
+		ch = p[2];
+		if (pc->st->l3.debug)
+			l3_debug(pc->st, "Restart for channel %d", chan);
+	}
+	dev_kfree_skb(skb, FREE_READ);
+	newl3state(pc, 2);
+	up = pc->st->l3.proc;
+	while (up) {
+		if ((ri & 7) == 7)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		else if (up->para.bchannel == chan)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		up = up->next;
+	}
+	p = tmp;
+	MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+	if (chan) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = ch | 0x80;
+	}
+	*p++ = 0x79;		/* RESTART Ind */
+	*p++ = 1;
+	*p++ = ri;
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 0);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
 }
 /* *INDENT-OFF* */
 static struct stateentry downstatelist[] =
 {
 	{SBIT(0),
-	 CC_SETUP_REQ, l3dss1_setup_req},
+	 CC_SETUP | REQUEST, l3dss1_setup_req},
+	{SBIT(0),
+	 CC_RESUME | REQUEST, l3dss1_resume_req},
 	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10),
-	 CC_DISCONNECT_REQ, l3dss1_disconnect_req},
+	 CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
 	{SBIT(12),
-	 CC_RELEASE_REQ, l3dss1_release_req},
+	 CC_RELEASE | REQUEST, l3dss1_release_req},
 	{ALL_STATES,
-	 CC_DLRL, l3dss1_reset},
+	 CC_DLRL | REQUEST, l3dss1_reset},
+	{ALL_STATES,
+	 CC_RESTART | REQUEST, l3dss1_restart},
 	{SBIT(6),
-	 CC_IGNORE, l3dss1_reset},
+	 CC_IGNORE | REQUEST, l3dss1_reset},
 	{SBIT(6),
-	 CC_REJECT_REQ, l3dss1_reject_req},
+	 CC_REJECT | REQUEST, l3dss1_reject_req},
 	{SBIT(6),
-	 CC_ALERTING_REQ, l3dss1_alert_req},
+	 CC_ALERTING | REQUEST, l3dss1_alert_req},
 	{SBIT(6) | SBIT(7),
-	 CC_SETUP_RSP, l3dss1_setup_rsp},
+	 CC_SETUP | RESPONSE, l3dss1_setup_rsp},
+	{SBIT(10),
+	 CC_SUSPEND | REQUEST, l3dss1_suspend_req},
 	{SBIT(1),
 	 CC_T303, l3dss1_t303},
 	{SBIT(2),
@@ -675,113 +1645,292 @@
 	 CC_T313, l3dss1_t313},
 	{SBIT(11),
 	 CC_T305, l3dss1_t305},
+	{SBIT(15),
+	 CC_T319, l3dss1_t319},
+	{SBIT(17),
+	 CC_T318, l3dss1_t318},
 	{SBIT(19),
 	 CC_T308_1, l3dss1_t308_1},
 	{SBIT(19),
 	 CC_T308_2, l3dss1_t308_2},
 };
 
-static int downsllen = sizeof(downstatelist) /
-sizeof(struct stateentry);
+#define DOWNSLLEN \
+	(sizeof(downstatelist) / sizeof(struct stateentry))
 
 static struct stateentry datastatelist[] =
 {
 	{ALL_STATES,
 	 MT_STATUS_ENQUIRY, l3dss1_status_enq},
+	{ALL_STATES,
+	 MT_FACILITY, l3dss1_facility},
+	{SBIT(19),
+	 MT_STATUS, l3dss1_release_ind},
+	{ALL_STATES,
+	 MT_STATUS, l3dss1_status},
 	{SBIT(0) | SBIT(6),
 	 MT_SETUP, l3dss1_setup},
 	{SBIT(1) | SBIT(2),
 	 MT_CALL_PROCEEDING, l3dss1_call_proc},
+	{SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_CALL_PROCEEDING, l3dss1_status_req},
 	{SBIT(1),
 	 MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack},
+	{SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_SETUP_ACKNOWLEDGE, l3dss1_status_req},
 	{SBIT(1) | SBIT(2) | SBIT(3),
 	 MT_ALERTING, l3dss1_alerting},
+	{SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_ALERTING, l3dss1_status_req},
 	{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),
+	{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),
+	{SBIT(19),  MT_RELEASE, l3dss1_release_ind},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(15),
 	 MT_DISCONNECT, l3dss1_disconnect},
+	{SBIT(11),
+	 MT_DISCONNECT, l3dss1_release_req},
 	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
 	 MT_CONNECT, l3dss1_connect},
+	{SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+	 MT_CONNECT, l3dss1_status_req},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(11) | SBIT(19),
+	 MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req},
 	{SBIT(8),
 	 MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
+	{SBIT(15),
+	 MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack},
+	{SBIT(15),
+	 MT_SUSPEND_REJECT, l3dss1_suspend_rej},
+	{SBIT(17),
+	 MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack},
+	{SBIT(17),
+	 MT_RESUME_REJECT, l3dss1_resume_rej},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(19),
+	 MT_INVALID, l3dss1_status_req},
 };
+
+#define DATASLLEN \
+	(sizeof(datastatelist) / sizeof(struct stateentry))
+
+static struct stateentry globalmes_list[] =
+{
+	{ALL_STATES,
+	 MT_STATUS, l3dss1_status},
+	{SBIT(0),
+	 MT_RESTART, l3dss1_global_restart},
+/*	{SBIT(1),
+	 MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
+*/
+};
+#define GLOBALM_LEN \
+	(sizeof(globalmes_list) / sizeof(struct stateentry))
 /* *INDENT-ON* */
 
 
-static int datasllen = sizeof(datastatelist) /
-sizeof(struct stateentry);
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+	int i;
+	struct l3_process *proc = st->l3.global;
+
+	for (i = 0; i < GLOBALM_LEN; i++)
+		if ((mt == globalmes_list[i].primitive) &&
+		    ((1 << proc->state) & globalmes_list[i].state))
+			break;
+	if (i == GLOBALM_LEN) {
+		dev_kfree_skb(skb, FREE_READ);
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1 global state %d mt %x unhandled",
+				proc->state, mt);
+		}
+		return;
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1 global %d mt %x",
+				proc->state, mt);
+		}
+		globalmes_list[i].rout(proc, mt, skb);
+	}
+}
 
 static void
 dss1up(struct PStack *st, int pr, void *arg)
 {
-	int i, mt;
+	int i, mt, cr, cause, callState;
+	char *ptr;
 	struct sk_buff *skb = arg;
-	char tmp[80];
+	struct l3_process *proc;
 
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+	}
 	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);
+			l3_debug(st, "dss1up%sunexpected discriminator %x message len %d",
+				 (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				 skb->data[0], skb->len);
 		}
-		SET_SKB_FREE(skb);
 		dev_kfree_skb(skb, FREE_READ);
 		return;
 	}
+	cr = getcallref(skb->data);
 	mt = skb->data[skb->data[1] + 2];
-	for (i = 0; i < datasllen; i++)
+	if (!cr) {		/* Global CallRef */
+		global_handler(st, mt, skb);
+		return;
+	} else if (cr == -1) {	/* Dummy Callref */
+		dev_kfree_skb(skb, FREE_READ);
+		return;
+	} else if (!(proc = getl3proc(st, cr))) {
+		/* No transaction process exist, that means no call with
+		 * this callreference is active
+		 */
+		if (mt == MT_SETUP) {
+			/* Setup creates a new transaction process */
+			if (!(proc = new_l3_process(st, cr))) {
+				/* May be to answer with RELEASE_COMPLETE and
+				 * CAUSE 0x2f "Resource unavailable", but this
+				 * need a new_l3_process too ... arghh
+				 */
+				dev_kfree_skb(skb, FREE_READ);
+				return;
+			}
+		} else if (mt == MT_STATUS) {
+			cause = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				cause = *ptr & 0x7f;
+			}
+			callState = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				callState = *ptr;
+			}
+			if (callState == 0) {
+				/* ETS 300-104 part 2.4.1
+				 * if setup has not been made and a message type
+				 * MT_STATUS is received with call state == 0,
+				 * we must send nothing
+				 */
+				dev_kfree_skb(skb, FREE_READ);
+				return;
+			} else {
+				/* ETS 300-104 part 2.4.2
+				 * if setup has not been made and a message type
+				 * MT_STATUS is received with call state != 0,
+				 * we must send MT_RELEASE_COMPLETE cause 101
+				 */
+				dev_kfree_skb(skb, FREE_READ);
+				if ((proc = new_l3_process(st, cr))) {
+					proc->para.cause = 0x65;	/* 101 */
+					l3dss1_msg_without_setup(proc, 0, NULL);
+				}
+				return;
+			}
+		} else if (mt == MT_RELEASE_COMPLETE) {
+			dev_kfree_skb(skb, FREE_READ);
+			return;
+		} else {
+			/* ETS 300-104 part 2
+			 * if setup has not been made and a message type
+			 * (except MT_SETUP and RELEASE_COMPLETE) is received,
+			 * we must send MT_RELEASE_COMPLETE cause 81 */
+			dev_kfree_skb(skb, FREE_READ);
+			if ((proc = new_l3_process(st, cr))) {
+				proc->para.cause = 0x51;	/* 81 */
+				l3dss1_msg_without_setup(proc, 0, NULL);
+			}
+			return;
+		}
+	} else if (!l3dss1_check_messagetype_validity(mt)) {
+		/* ETS 300-104 7.4.2, 8.4.2, 10.3.2, 11.4.2, 12.4.2, 13.4.2,
+		 * 14.4.2...
+		 * if setup has been made and invalid message type is received,
+		 * we must send MT_STATUS cause 0x62
+		 */
+		mt = MT_INVALID;	/* sorry, not clean, but do the right thing ;-) */
+	}
+	for (i = 0; i < DATASLLEN; i++)
 		if ((mt == datastatelist[i].primitive) &&
-		    ((1 << st->l3.state) & datastatelist[i].state))
+		    ((1 << proc->state) & datastatelist[i].state))
 			break;
-	if (i == datasllen) {
-		SET_SKB_FREE(skb);
+	if (i == DATASLLEN) {
 		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);
+			l3_debug(st, "dss1up%sstate %d mt %x unhandled",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
 		}
 		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);
+			l3_debug(st, "dss1up%sstate %d mt %x",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
 		}
-		datastatelist[i].rout(st, pr, skb);
+		datastatelist[i].rout(proc, pr, skb);
 	}
 }
 
 static void
 dss1down(struct PStack *st, int pr, void *arg)
 {
-	int i;
-	char tmp[80];
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
 
-	for (i = 0; i < downsllen; i++)
+	if (((DL_ESTABLISH | REQUEST) == pr) || ((DL_RELEASE | REQUEST) == pr)) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if ((proc = new_l3_process(st, cr))) {
+			proc->chan = chan;
+			chan->proc = proc;
+			proc->para.setup = chan->setup;
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+	if (!proc) {
+		printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr);
+		return;
+	}
+	for (i = 0; i < DOWNSLLEN; i++)
 		if ((pr == downstatelist[i].primitive) &&
-		    ((1 << st->l3.state) & downstatelist[i].state))
+		    ((1 << proc->state) & downstatelist[i].state))
 			break;
-	if (i == downsllen) {
+	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);
+			l3_debug(st, "dss1down state %d prim %d unhandled",
+				proc->state, pr);
 		}
 	} else {
 		if (st->l3.debug & L3_DEB_STATE) {
-			sprintf(tmp, "dss1down state %d prim %d",
-				st->l3.state, pr);
-			l3_debug(st, tmp);
+			l3_debug(st, "dss1down state %d prim %d",
+				proc->state, pr);
 		}
-		downstatelist[i].rout(st, pr, arg);
+		downstatelist[i].rout(proc, pr, arg);
 	}
 }
 
@@ -790,20 +1939,20 @@
 {
 	char tmp[64];
 
-	st->l4.l4l3 = dss1down;
+	st->lli.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));
+	st->l3.N303 = 1;
+	if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n");
+	} else {
+		st->l3.global->state = 0;
+		st->l3.global->callref = 0;
+		st->l3.global->next = NULL;
+		st->l3.global->debug = L3_DEB_WARN;
+		st->l3.global->st = st;
+		st->l3.global->N303 = 1;
+		L3InitTimer(st->l3.global, &st->l3.global->timer);
 	}
+	strcpy(tmp, dss1_revision);
+	printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/l3dss1.h linux/drivers/isdn/hisax/l3dss1.h
--- v2.0.35/linux/drivers/isdn/hisax/l3dss1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3dss1.h	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,75 @@
+/* $Id: l3dss1.h,v 1.3.2.3 1998/05/27 18:06:14 keil Exp $
+ *
+ *  DSS1 (Euro) D-channel protocol defines
+ *
+ * $Log: l3dss1.h,v $
+ * Revision 1.3.2.3  1998/05/27 18:06:14  keil
+ * HiSax 3.0
+ *
+ * Revision 1.3.2.2  1998/02/03 23:16:10  keil
+ * german AOC
+ *
+ * Revision 1.3.2.1  1997/10/17 22:10:52  keil
+ * new files on 2.0
+ *
+ * Revision 1.3  1997/08/07 17:44:37  keil
+ * Fix RESTART
+ *
+ * Revision 1.2  1997/08/03 14:36:34  keil
+ * Implement RESTART procedure
+ *
+ * Revision 1.1  1997/07/27 21:08:38  keil
+ * new
+ *
+ *
+ *
+ */
+#define T303	4000
+#define T304	30000
+#define T305	30000
+#define T308	4000
+#define T310	30000
+#define T313	4000
+#define T318	4000
+#define T319	4000
+
+/*
+ * 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 MT_INVALID             0xff
+
+#define IE_BEARER              0x04
+#define IE_CAUSE               0x08
+#define IE_CALLID              0x10
+#define IE_FACILITY            0x1c
+#define IE_CALL_STATE          0x14
+#define IE_CHANNEL_ID          0x18
+#define IE_RESTART_IND         0x79
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/lmgr.c linux/drivers/isdn/hisax/lmgr.c
--- v2.0.35/linux/drivers/isdn/hisax/lmgr.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/lmgr.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,69 @@
+/* $Id: lmgr.c,v 1.1.2.5 1998/11/03 00:07:21 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ *  Layermanagement module
+ *
+ * $Log: lmgr.c,v $
+ * Revision 1.1.2.5  1998/11/03 00:07:21  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.4  1998/05/27 18:06:15  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.3  1998/03/07 23:15:37  tsbogend
+ * made HiSax working on Linux/Alpha
+ *
+ * Revision 1.1.2.2  1997/11/15 18:54:19  keil
+ * cosmetics
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:53  keil
+ * new files on 2.0
+ *
+ * Revision 1.1  1997/06/26 11:17:25  keil
+ * first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+
+static void
+error_handling_dchan(struct PStack *st, int Error)
+{
+	switch (Error) {
+		case 'C':
+		case 'D':
+		case 'G':
+		case 'H':
+			st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL);
+			break;
+	}
+}
+
+static void
+hisax_manager(struct PStack *st, int pr, void *arg)
+{
+	long Code;
+
+	switch (pr) {
+		case (MDL_ERROR | INDICATION):
+			Code = (long) arg;
+			HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR",
+				"%c %s\n", (char)Code, 
+				test_bit(FLG_LAPD, &st->l2.flag) ?
+				"D-channel" : "B-channel");
+			if (test_bit(FLG_LAPD, &st->l2.flag))
+				error_handling_dchan(st, Code);
+			break;
+	}
+}
+
+void
+setstack_manager(struct PStack *st)
+{
+	st->ma.layer = hisax_manager;
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc
--- v2.0.35/linux/drivers/isdn/hisax/md5sums.asc	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/md5sums.asc	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,29 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+# This are valid md5sums for certificated HiSax driver.
+# The certification is valid only if the md5sums of all files match.
+# The certification is valid only for ELSA QuickStep cards in the moment.
+# Read ../../../Documentation/isdn/HiSax.cert for more informations.
+# 
+9bf10ea00044ef68391c9511cf96f9dc  isac.c
+1aa8f88a428a5a522c9256133b510004  isdnl1.c
+b8382eb8a6d94bca10638f250cf441e0  isdnl2.c
+3d7ebf2051d1a5aa4aacaf924a2442b2  isdnl3.c
+15f5c1930545bbb8ec56bf905ae968ef  tei.c
+3cc38f121e5154c22536ec1f6e179fbf  callc.c
+1741371ba3e08468243bea3ce8a7820e  cert.c
+22849af1bcb15860e8426e416a037dac  l3dss1.c
+1a8a9f57880c23db0f49c7f141f546a1  l3_1tr6.c
+b9d329bd178854b86ca37116889f7f3e  elsa.c
+# end of md5sums
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBNkIZQjpxHvX/mS9tAQEEZgP8DX7o3Y241UyCqPpgxB7J4NyWAg+p/wmF
+NvYAMVLk10YZqUvA2h8kP7wXawNI1nR0Aj6fFz7Qf7KBcrPnSAxa9rwP32fuwrmx
+qG2T0KUNxBhADP6lL6WuKRKA9NQ7a0nJVkrnO+yD+xq7eDHQQcm6zTrL02ogRR+1
+T1p8akJTsRA=
+=yWR0
+-----END PGP SIGNATURE-----
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/mic.c linux/drivers/isdn/hisax/mic.c
--- v2.0.35/linux/drivers/isdn/hisax/mic.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/mic.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,278 @@
+/* $Id: mic.c,v 1.1.2.5 1998/04/08 21:58:43 keil Exp $
+
+ * mic.c  low level stuff for mic cards
+ *
+ * Copyright (C) 1997 
+ *
+ * Author  Stephan von Krawczynski <skraw@ithnet.com>
+ *
+ *
+ * $Log: mic.c,v $
+ * Revision 1.1.2.5  1998/04/08 21:58:43  keil
+ * New init code
+ *
+ * Revision 1.1.2.4  1998/02/17 15:39:20  keil
+ * fix reset problem
+ *
+ * Revision 1.1.2.3  1998/01/27 22:37:25  keil
+ * fast io
+ *
+ * Revision 1.1.2.2  1997/11/15 18:50:54  keil
+ * new common init function
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:54  keil
+ * new files on 2.0
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *mic_revision = "$Revision: 1.1.2.5 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define MIC_ISAC	2
+#define MIC_HSCX	1
+#define MIC_ADR		7
+
+/* CARD_ADR (Write) */
+#define MIC_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr,
+			cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr,
+		 cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+mic_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "mic: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_mic(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.mic.cfg_reg)
+		release_region(cs->hw.mic.cfg_reg, bytecnt);
+}
+
+static int
+mic_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_mic(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &mic_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscx(cs); /* /RTSA := ISAC RST */
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_mic(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, mic_revision);
+	printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_MIC)
+		return (0);
+
+	bytecnt = 8;
+	cs->hw.mic.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR;
+	cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC;
+	cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX;
+
+	if (check_region((cs->hw.mic.cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.mic.cfg_reg,
+		       cs->hw.mic.cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn");
+	}
+
+	printk(KERN_INFO
+	       "mic: defined at 0x%x IRQ %d\n",
+	       cs->hw.mic.cfg_reg,
+	       cs->irq);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &mic_card_msg;
+	ISACVersion(cs, "mic:");
+	if (HscxVersion(cs, "mic:")) {
+		printk(KERN_WARNING
+		    "mic: wrong HSCX versions check IO address\n");
+		release_io_mic(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/netjet.c linux/drivers/isdn/hisax/netjet.c
--- v2.0.35/linux/drivers/isdn/hisax/netjet.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/netjet.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,1154 @@
+/* $Id: netjet.c,v 1.1.2.10 1998/11/03 00:07:24 keil Exp $
+
+ * netjet.c     low level stuff for Traverse Technologie NETJet ISDN cards
+ *
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ * Thanks to Traverse Technologie Australia for documents and informations
+ *
+ *
+ * $Log: netjet.c,v $
+ * Revision 1.1.2.10  1998/11/03 00:07:24  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.9  1998/09/30 22:24:02  keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.1.2.8  1998/09/27 13:06:56  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.1.2.7  1998/05/27 18:06:17  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.6  1998/04/08 22:05:23  keil
+ * Forgot PCI fix
+ *
+ * Revision 1.1.2.5  1998/04/08 21:49:29  keil
+ * New init; fix PCI for more as one card
+ *
+ * Revision 1.1.2.4  1998/01/27 22:37:27  keil
+ * fast io
+ *
+ * Revision 1.1.2.3  1997/12/01 09:09:57  keil
+ * IRQ bit clearing
+ *
+ * Revision 1.1.2.2  1997/11/27 12:32:01  keil
+ * Working netjet driver
+ *
+ * Revision 1.1.2.1  1997/11/15 18:58:13  keil
+ * new card
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+
+#ifndef bus_to_virt
+#define bus_to_virt (u_int *)
+#endif
+
+#ifndef virt_to_bus
+#define virt_to_bus (u_int)
+#endif
+
+extern const char *CardType[];
+
+const char *NETjet_revision = "$Revision: 1.1.2.10 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/* PCI stuff */
+#define PCI_VENDOR_TRAVERSE_TECH 0xe159
+#define PCI_NETJET_ID	0x0001
+
+#define NETJET_CTRL	0x00
+#define NETJET_DMACTRL	0x01
+#define NETJET_AUXCTRL	0x02
+#define NETJET_AUXDATA	0x03
+#define NETJET_IRQMASK0 0x04
+#define NETJET_IRQMASK1 0x05
+#define NETJET_IRQSTAT0 0x06
+#define NETJET_IRQSTAT1 0x07
+#define NETJET_DMA_READ_START	0x08
+#define NETJET_DMA_READ_IRQ	0x0c
+#define NETJET_DMA_READ_END	0x10
+#define NETJET_DMA_READ_ADR	0x14
+#define NETJET_DMA_WRITE_START	0x18
+#define NETJET_DMA_WRITE_IRQ	0x1c
+#define NETJET_DMA_WRITE_END	0x20
+#define NETJET_DMA_WRITE_ADR	0x24
+#define NETJET_PULSE_CNT	0x28
+
+#define NETJET_ISAC_OFF	0xc0
+#define NETJET_ISACIRQ	0x10
+#define NETJET_IRQM0_READ	0x0c
+#define NETJET_IRQM0_READ_1	0x04
+#define NETJET_IRQM0_READ_2	0x08
+#define NETJET_IRQM0_WRITE	0x03
+#define NETJET_IRQM0_WRITE_1	0x01
+#define NETJET_IRQM0_WRITE_2	0x02
+
+#define NETJET_DMA_SIZE 512
+
+#define HDLC_ZERO_SEARCH 0
+#define HDLC_FLAG_SEARCH 1
+#define HDLC_FLAG_FOUND  2
+#define HDLC_FRAME_FOUND 3
+#define HDLC_NULL 4
+#define HDLC_PART 5
+#define HDLC_FULL 6
+
+#define HDLC_FLAG_VALUE	0x7e
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	long flags;
+	u_char ret;
+	
+	save_flags(flags);
+	cli();
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2));
+	restore_flags(flags);
+	return(ret);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	long flags;
+	
+	save_flags(flags);
+	cli();
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value);
+	restore_flags(flags);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	insb(cs->hw.njet.isac, data, size);
+}
+
+__u16 fcstab[256] =
+{
+	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+static void 
+WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	outsb(cs->hw.njet.isac, data, size);
+}
+
+void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill)
+{
+	u_int mask=0x000000ff, val = 0, *p=pos;
+	u_int i;
+	
+	val |= fill;
+	if (chan) {
+		val  <<= 8;
+		mask <<= 8;
+	}
+	mask ^= 0xffffffff;
+	for (i=0; i<cnt; i++) {
+		*p   &= mask;
+		*p++ |= val;
+		if (p > bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send;
+	}
+}
+
+void
+mode_tiger(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "Tiger mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_SIZE, bc, 0xff);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "Tiger stat rec %d/%d send %d",
+					bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err,
+					bcs->hw.tiger.s_tot); 
+			if ((cs->bcs[0].mode == L1_MODE_NULL) &&
+				(cs->bcs[1].mode == L1_MODE_NULL)) {
+				cs->hw.njet.dmactrl = 0;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+			}
+			break;
+		case (L1_MODE_TRANS):
+			break;
+		case (L1_MODE_HDLC): 
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_SIZE, bc, 0xff);
+			bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot = 0;
+			bcs->hw.tiger.r_bitcnt = 0;
+			bcs->hw.tiger.r_one = 0;
+			bcs->hw.tiger.r_err = 0;
+			bcs->hw.tiger.s_tot = 0;
+			if (! cs->hw.njet.dmactrl) {
+				fill_mem(bcs, bcs->hw.tiger.send,
+					NETJET_DMA_SIZE, !bc, 0xff);
+				cs->hw.njet.dmactrl = 1;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x3f);
+			}
+			bcs->hw.tiger.sendp = bcs->hw.tiger.send;
+			bcs->hw.tiger.free = NETJET_DMA_SIZE;
+			test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+			break;
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "tiger: set %x %x %x  %x/%x  pulse=%d",
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			bytein(cs->hw.njet.base + NETJET_IRQSTAT0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+}
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) {
+	char tmp[128];
+	char *t = tmp;
+	int i=count,j;
+	u_char *p = buf;
+
+	t += sprintf(t, "tiger %s(%4d)", s, count);
+	while (i>0) {
+		if (i>16)
+			j=16;
+		else
+			j=i;
+		QuickHex(t, p, j);
+		debugl1(cs, tmp);
+		p += j;
+		i -= j;
+		t = tmp;
+		t += sprintf(t, "tiger %s      ", s);
+	}
+}
+
+#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \
+			bitcnt++;\
+			s_val >>= 1;\
+			if (val & 1) {\
+				s_one++;\
+				s_val |= 0x80;\
+			} else {\
+				s_one = 0;\
+				s_val &= 0x7f;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			if (s_one == 5) {\
+				s_val >>= 1;\
+				s_val &= 0x7f;\
+				bitcnt++;\
+				s_one = 0;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			val >>= 1;\
+		}
+
+static int make_raw_data(struct BCState *bcs) {
+	register u_int i,s_cnt=0;
+	register u_char j;
+	register u_char val;
+	register u_char s_one = 0;
+	register u_char s_val = 0;
+	register u_char bitcnt = 0;
+	u_int fcs;
+	
+	if (!bcs->tx_skb) {
+		debugl1(bcs->cs, "tiger make_raw: NULL skb");
+		return(1);
+	}
+	bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE;
+	fcs = PPP_INITFCS;
+	for (i=0; i<bcs->tx_skb->len; i++) {
+		val = bcs->tx_skb->data[i];
+		fcs = PPP_FCS (fcs, val);
+		MAKE_RAW_BYTE;
+	}
+	fcs ^= 0xffff;
+	val = fcs & 0xff;
+	MAKE_RAW_BYTE;
+	val = (fcs>>8) & 0xff;
+	MAKE_RAW_BYTE;
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==8) {
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger make_raw: in %ld out %d.%d",
+			bcs->tx_skb->len, s_cnt, bitcnt);
+	if (bitcnt) {
+		while (8>bitcnt++) {
+			s_val >>= 1;
+			s_val |= 0x80;
+		}
+		bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+	}
+	bcs->hw.tiger.sendcnt = s_cnt;
+	bcs->tx_cnt -= bcs->tx_skb->len;
+	bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+	return(0);
+}
+
+static void got_frame(struct BCState *bcs, int count) {
+	struct sk_buff *skb;
+		
+	if (!(skb = dev_alloc_skb(count)))
+		printk(KERN_WARNING "TIGER: receive out of memory\n");
+	else {
+		SET_SKB_FREE(skb);
+		memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count);
+		skb_queue_tail(&bcs->rqueue, skb);
+	}
+	bcs->event |= 1 << B_RCVBUFREADY;
+	queue_task(&bcs->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	
+	if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME)
+		printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec");
+}
+
+
+
+static void read_raw(struct BCState *bcs, u_int *buf, int cnt){
+	int i;
+	register u_char j;
+	register u_char val;
+	u_int  *pend = bcs->hw.tiger.rec +NETJET_DMA_SIZE -1;
+	register u_char state = bcs->hw.tiger.r_state;
+	register u_char r_one = bcs->hw.tiger.r_one;
+	register u_char r_val = bcs->hw.tiger.r_val;
+	register u_int bitcnt = bcs->hw.tiger.r_bitcnt;
+	u_int *p = buf;
+        
+	for (i=0;i<cnt;i++) {
+		val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff);
+		p++;
+		if (p > pend)
+			p = bcs->hw.tiger.rec;
+		if (val == 0xff) {
+			state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot++;
+			bitcnt = 0;
+			r_one = 0;
+			continue;
+		}
+		for (j=0;j<8;j++) {
+			if (state == HDLC_ZERO_SEARCH) {
+				if (val & 1) {
+					r_one++;
+				} else {
+					r_one=0;
+					state= HDLC_FLAG_SEARCH;
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: zBit(%d,%d,%d) %x",
+							bcs->hw.tiger.r_tot,i,j,val);
+				}
+			} else if (state == HDLC_FLAG_SEARCH) { 
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						state=HDLC_FLAG_FOUND;
+						if (bcs->cs->debug & L1_DEB_HSCX)
+							debugl1(bcs->cs,"tiger read_raw: flag(%d,%d,%d) %x",
+								bcs->hw.tiger.r_tot,i,j,val);
+					}
+					r_one=0;
+				}
+			} else if (state ==  HDLC_FLAG_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						r_one=0;
+						val >>= 1;
+						continue;
+					} else if (r_one!=5) {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state != HDLC_ZERO_SEARCH) &&
+					!(bitcnt & 7)) {
+					state=HDLC_FRAME_FOUND;
+					bcs->hw.tiger.r_fcs = PPP_INITFCS;
+					bcs->hw.tiger.rcvbuf[0] = r_val;
+					bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
+							bcs->hw.tiger.r_tot,i,j,r_val,val,
+							bcs->cs->hw.njet.irqstat0);
+				}
+			} else if (state ==  HDLC_FRAME_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+						bitcnt=0;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						r_val=0; 
+						r_one=0;
+						bitcnt++;
+						if (bitcnt & 7) {
+							debugl1(bcs->cs, "tiger: frame not byte aligned");
+							state=HDLC_FLAG_SEARCH;
+							bcs->hw.tiger.r_err++;
+						} else {
+							if (bcs->cs->debug & L1_DEB_HSCX)
+								debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x",
+									i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
+							if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
+								got_frame(bcs, (bitcnt>>3)-3);
+							} else
+								if (bcs->cs->debug) {
+									debugl1(bcs->cs, "tiger FCS error");
+									printframe(bcs->cs, bcs->hw.tiger.rcvbuf,
+										(bitcnt>>3)-1, "rec");
+									bcs->hw.tiger.r_err++;
+								}
+							state=HDLC_FLAG_FOUND;
+						}
+						bitcnt=0;
+					} else if (r_one==5) {
+						val >>= 1;
+						r_one=0;
+						continue;
+					} else {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state == HDLC_FRAME_FOUND) &&
+					!(bitcnt & 7)) {
+					if ((bitcnt>>3)>=HSCX_BUFMAX) {
+						debugl1(bcs->cs, "tiger: frame to big");
+						r_val=0; 
+						state=HDLC_FLAG_SEARCH;
+						bcs->hw.tiger.r_err++;
+					} else {
+						bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val;
+						bcs->hw.tiger.r_fcs = 
+							PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					}
+				}
+			}
+			val >>= 1;
+		}
+		bcs->hw.tiger.r_tot++;
+	}
+	bcs->hw.tiger.r_state = state;
+	bcs->hw.tiger.r_one = r_one;
+	bcs->hw.tiger.r_val = r_val;
+	bcs->hw.tiger.r_bitcnt = bitcnt;
+}
+
+static void read_tiger(struct IsdnCardState *cs) {
+	u_int *p;
+	int cnt = NETJET_DMA_SIZE/2;
+	
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) {
+		debugl1(cs,"tiger warn read double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ);
+	}	
+	if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1)
+		p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.rec + cnt - 1;
+	if (cs->bcs[0].mode == L1_MODE_HDLC)
+		read_raw(cs->bcs, p, cnt);
+	if (cs->bcs[1].mode == L1_MODE_HDLC)
+		read_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ;
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
+
+static void fill_dma(struct BCState *bcs)
+{
+	register u_int *p, *sp;
+	register int cnt;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma1: c%d %4x", bcs->channel,
+			bcs->Flag);
+	if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag))
+		return;
+	if (make_raw_data(bcs))
+		return;		
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma2: c%d %4x", bcs->channel,
+			bcs->Flag);
+	if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		sp = bcs->hw.tiger.sendp;
+		if (p == bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send -1;
+		if (sp == bcs->hw.tiger.s_end)
+			sp = bcs->hw.tiger.send -1;
+		cnt = p - sp;
+		if (cnt <0) {
+			write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+		} else {
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			write_raw(bcs, p, bcs->hw.tiger.free - cnt);
+		}
+	} else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		cnt = bcs->hw.tiger.s_end - p;
+		if (cnt < 2) {
+			p = bcs->hw.tiger.send + 1;
+			cnt = NETJET_DMA_SIZE/2 - 2;
+		} else {
+			p++;
+			p++;
+			if (cnt <= (NETJET_DMA_SIZE/2))
+				cnt += NETJET_DMA_SIZE/2;
+			cnt--;
+			cnt--;
+		}
+		write_raw(bcs, p, cnt);
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma3: c%d %4x", bcs->channel,
+			bcs->Flag);
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
+	u_int mask, val, *p=buf;
+	u_int i, s_cnt;
+        
+        if (cnt <= 0)
+        	return;
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		if (bcs->hw.tiger.sendcnt> cnt) {
+			s_cnt = cnt;
+			bcs->hw.tiger.sendcnt -= cnt;
+		} else {
+			s_cnt = bcs->hw.tiger.sendcnt;
+			bcs->hw.tiger.sendcnt = 0;
+		}
+		if (bcs->channel)
+			mask = 0xffff00ff;
+		else
+			mask = 0xffffff00;
+		for (i=0; i<s_cnt; i++) {
+			val = bcs->channel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) :
+				(bcs->hw.tiger.sp[i]);
+			*p   &= mask;
+			*p++ |= val;
+			if (p>bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+		}
+		bcs->hw.tiger.s_tot += s_cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel,
+				(u_int)buf, (u_int)p, s_cnt, cnt,
+				bcs->hw.tiger.sendcnt, bcs->cs->hw.njet.irqstat0);
+		if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
+			printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd");
+		bcs->hw.tiger.sp += s_cnt;
+		bcs->hw.tiger.sendp = p;
+		if (!bcs->hw.tiger.sendcnt) {
+			if (!bcs->tx_skb) {
+				debugl1(bcs->cs,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
+			} else {
+				if (bcs->st->lli.l1writewakeup &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type))
+					bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
+				dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+				bcs->tx_skb = NULL;
+			}
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.tiger.free = cnt - s_cnt;
+			if (bcs->hw.tiger.free > (NETJET_DMA_SIZE/2))
+				test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+			else {
+				test_and_clear_bit(BC_FLG_HALF, &bcs->Flag);
+				test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
+			}
+			if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+				fill_dma(bcs);
+			} else {
+				mask ^= 0xffffffff;
+				if (s_cnt < cnt) {
+					for (i=s_cnt; i<cnt;i++) {
+						*p++ |= mask;
+						if (p>bcs->hw.tiger.s_end)
+							p = bcs->hw.tiger.send;
+					}
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "tiger write_raw: fill rest %d",
+							cnt - s_cnt);
+				}
+				bcs->event |= 1 << B_XMTBUFREADY;
+				queue_task(&bcs->tqueue, &tq_immediate);
+				mark_bh(IMMEDIATE_BH);
+			}
+		}
+	} else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		bcs->hw.tiger.free += cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill half");
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill full");
+	}
+}
+
+static void write_tiger(struct IsdnCardState *cs) {
+	u_int *p, cnt = NETJET_DMA_SIZE/2;
+	
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) {
+		debugl1(cs,"tiger warn write double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE);
+	}	
+	if (cs->hw.njet.irqstat0  & NETJET_IRQM0_WRITE_1)
+		p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.send + cnt - 1;
+	if (cs->bcs[0].mode == L1_MODE_HDLC)
+		write_raw(cs->bcs, p, cnt);
+	if (cs->bcs[1].mode == L1_MODE_HDLC)
+		write_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE;
+}
+
+static void
+tiger_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			save_flags(flags);
+			cli();
+			if (st->l1.bcs->tx_skb) {
+				skb_queue_tail(&st->l1.bcs->squeue, skb);
+				restore_flags(flags);
+			} else {
+				st->l1.bcs->tx_skb = skb;
+				st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_PULL | INDICATION):
+			if (st->l1.bcs->tx_skb) {
+				printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n");
+				break;
+			}
+			save_flags(flags);
+			cli();
+			st->l1.bcs->tx_skb = skb;
+			st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+			restore_flags(flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!st->l1.bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+			mode_tiger(st->l1.bcs, 0, st->l1.bc);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+
+void
+close_tigerstate(struct BCState *bcs)
+{
+	mode_tiger(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.tiger.rcvbuf) {
+			kfree(bcs->hw.tiger.rcvbuf);
+			bcs->hw.tiger.rcvbuf = NULL;
+		}
+		if (bcs->hw.tiger.sendbuf) {
+			kfree(bcs->hw.tiger.sendbuf);
+			bcs->hw.tiger.sendbuf = NULL;
+		}
+		discard_queue(&bcs->rqueue);
+		discard_queue(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb(bcs->tx_skb, FREE_WRITE);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_tigerstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.sendbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	bcs->hw.tiger.sendcnt = 0;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_tiger(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_tigerstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = tiger_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+ 
+__initfunc(void
+inittiger(struct IsdnCardState *cs))
+{
+	if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.send\n");
+		return;
+	}
+	cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE/2 - 1;
+	cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1;
+	cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send;
+	cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq;
+	cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end;
+	
+	memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
+	debugl1(cs, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send,
+		(u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1));
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.send),
+		cs->hw.njet.base + NETJET_DMA_READ_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq),
+		cs->hw.njet.base + NETJET_DMA_READ_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end),
+		cs->hw.njet.base + NETJET_DMA_READ_END);
+	if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.rec\n");
+		return;
+	}
+	debugl1(cs, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec,
+		(u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1));
+	cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec;
+	memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec),
+		cs->hw.njet.base + NETJET_DMA_WRITE_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE/2 - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_END);
+	debugl1(cs, "tiger: dmacfg  %x/%x  pulse=%d",
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+		inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+		bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+	cs->hw.njet.last_is0 = 0;
+	cs->bcs[0].BC_SetStack = setstack_tiger;
+	cs->bcs[1].BC_SetStack = setstack_tiger;
+	cs->bcs[0].BC_Close = close_tigerstate;
+	cs->bcs[1].BC_Close = close_tigerstate;
+}
+
+void
+releasetiger(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.tiger.send) {
+		kfree(cs->bcs[0].hw.tiger.send);
+		cs->bcs[0].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.send) {
+		cs->bcs[1].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[0].hw.tiger.rec) {
+		kfree(cs->bcs[0].hw.tiger.rec);
+		cs->bcs[0].hw.tiger.rec = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.rec) {
+		cs->bcs[1].hw.tiger.rec = NULL;
+	}
+}
+
+static void
+netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	long flags;
+
+	if (!cs) {
+		printk(KERN_WARNING "NETjet: Spurious interrupt!\n");
+		return;
+	}
+	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
+		NETJET_ISACIRQ)) {
+		val = ReadISAC(cs, ISAC_ISTA);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", sval, val);
+		if (val) {
+			isac_interrupt(cs, val);
+			WriteISAC(cs, ISAC_MASK, 0xFF);
+			WriteISAC(cs, ISAC_MASK, 0x0);
+		}
+	}
+	save_flags(flags);
+	cli();
+	if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			restore_flags(flags);
+			return;
+		}
+		cs->hw.njet.irqstat0 = sval;
+		restore_flags(flags);
+/*		debugl1(cs, "tiger: ist0 %x  %x %x  %x/%x  pulse=%d",
+			sval, 
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+*/
+/*		cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30;
+*/		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0);
+/*		byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0);
+*/		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ)
+			read_tiger(cs);
+		if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE)
+			write_tiger(cs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		restore_flags(flags);
+
+/*	if (!testcnt--) {
+		cs->hw.njet.dmactrl = 0;
+		byteout(cs->hw.njet.base + NETJET_DMACTRL,
+			cs->hw.njet.dmactrl);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	}
+*/
+}
+
+static void
+reset_netjet(struct IsdnCardState *cs)
+{
+	long flags;
+
+	save_flags(flags);
+	sti();
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + (10 * HZ) / 1000;	/* Timeout 10ms */
+	schedule();
+	restore_flags(flags);
+	cs->hw.njet.auxd = 0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+void
+release_io_netjet(struct IsdnCardState *cs)
+{
+	byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0);
+	releasetiger(cs);
+	release_region(cs->hw.njet.base, 256);
+}
+
+
+static int
+NETjet_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_netjet(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &netjet_interrupt,
+					I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs));
+		case CARD_INIT:
+			inittiger(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_netjet(struct IsdnCard *card))
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+#if CONFIG_PCI
+	u_char pci_bus, pci_device_fn, pci_irq;
+	u_int pci_ioaddr, found;
+#endif
+
+	strcpy(tmp, NETjet_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET)
+		return(0);
+#if CONFIG_PCI
+	found = 0;
+	for (; pci_index < 0xff; pci_index++) {
+		if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH,
+			PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn)
+			== PCIBIOS_SUCCESSFUL)
+			found = 1;
+		else
+			break;
+		/* get IRQ */
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+			PCI_INTERRUPT_LINE, &pci_irq);
+
+		/* get IO address */
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+			PCI_BASE_ADDRESS_0, &pci_ioaddr);
+		if (found)
+			break;
+	}
+	if (!found) {
+		printk(KERN_WARNING "NETjet: No PCI card found\n");
+		return(0);
+	}
+	pci_index++;
+	if (!pci_irq) {
+		printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n");
+		return(0);
+	}
+	if (!pci_ioaddr) {
+		printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n");
+		return(0);
+	}
+	pci_ioaddr &= ~3; /* remove io/mem flag */
+	cs->hw.njet.base = pci_ioaddr; 
+	cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA;
+	cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF;
+	cs->irq = pci_irq;
+	bytecnt = 256;
+#else
+	printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n");
+	return (0);
+#endif /* CONFIG_PCI */
+	printk(KERN_INFO
+		"NETjet: PCI card configured at 0x%x IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (check_region(cs->hw.njet.base, bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	} else {
+		request_region(cs->hw.njet.base, bytecnt, "netjet isdn");
+	}
+	reset_netjet(cs);
+	cs->readisac  = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo  = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &fill_dma;
+	cs->cardmsg = &NETjet_card_msg;
+	ISACVersion(cs, "NETjet:");
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/niccy.c linux/drivers/isdn/hisax/niccy.c
--- v2.0.35/linux/drivers/isdn/hisax/niccy.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/niccy.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,406 @@
+/* $Id: niccy.c,v 1.1.2.4 1998/04/16 19:18:19 keil Exp $
+
+ * niccy.c  low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
+ *          compatible (SAGEM cybermodem)
+ *
+ * Author   Karsten Keil
+ * 
+ * Thanks to Dr. Neuhaus and SAGEM for informations
+ *
+ * $Log: niccy.c,v $
+ * Revision 1.1.2.4  1998/04/16 19:18:19  keil
+ * need config.h
+ *
+ * Revision 1.1.2.3  1998/04/08 22:05:26  keil
+ * Forgot PCI fix
+ *
+ * Revision 1.1.2.2  1998/04/08 21:48:23  keil
+ * New init; working Niccy PCI
+ *
+ * Revision 1.1.2.1  1998/02/11 14:23:20  keil
+ * support for Dr Neuhaus Niccy PnP and PCI
+ *
+ *
+ */
+
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+extern const char *CardType[];
+const char *niccy_revision = "$Revision: 1.1.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_PCI_DATA	0
+#define HSCX_PCI_DATA	1
+#define ISAC_PCI_ADDR	2
+#define HSCX_PCI_ADDR	3
+#define ISAC_PNP	0
+#define HSCX_PNP	1
+
+/* SUB Types */
+#define NICCY_PNP	1
+#define NICCY_PCI	2
+
+/* PCI stuff */
+#define PCI_VENDOR_DR_NEUHAUS	0x1267
+#define PCI_NICCY_ID		0x1016
+#define PCI_IRQ_CTRL_REG	0x38
+#define PCI_IRQ_ENABLE		0x1f00
+#define PCI_IRQ_DISABLE		0xff0000
+#define PCI_IRQ_ASSERT		0x800000
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.niccy.hscx_ale,
+			cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.hscx_ale,
+		 cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "Niccy: Spurious interrupt!\n");
+		return;
+	}
+	if (cs->subtyp == NICCY_PCI) {
+		int ival;
+		ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		if (!(ival & PCI_IRQ_ASSERT)) /* IRQ not for us (shared) */
+			return;
+		outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	}
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0);
+		writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0);
+	}
+}
+
+void
+release_io_niccy(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == NICCY_PCI) {
+		int val;
+		
+		val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		val &= PCI_IRQ_DISABLE;
+		outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		release_region(cs->hw.niccy.cfg_reg, 0x80);
+		release_region(cs->hw.niccy.isac, 4);
+	} else {
+		release_region(cs->hw.niccy.isac, 2);
+		release_region(cs->hw.niccy.isac_ale, 2);
+	}
+}
+
+static void
+niccy_reset(struct IsdnCardState *cs)
+{
+	int val, nval;
+	
+	val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	nval = val | PCI_IRQ_ENABLE;
+	outl(nval, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+
+	inithscxisac(cs, 3);
+}
+
+static int
+niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	int imode;
+	
+	switch (mt) {
+		case CARD_RESET:
+			niccy_reset(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_niccy(cs);
+			return(0);
+		case CARD_SETIRQ:
+			if (cs->subtyp == NICCY_PCI)
+				imode = I4L_IRQ_FLAG | SA_SHIRQ;
+			else
+				imode = I4L_IRQ_FLAG;
+			return(request_irq(cs->irq, &niccy_interrupt,
+				imode, "HiSax", cs));
+			break;
+		case CARD_INIT:
+			niccy_reset(cs);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_niccy(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, niccy_revision);
+	printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NICCY)
+		return (0);
+
+	if (card->para[1]) {
+		cs->hw.niccy.isac = card->para[1] + ISAC_PNP;
+		cs->hw.niccy.hscx = card->para[1] + HSCX_PNP;
+		cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP;
+		cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP;
+		cs->hw.niccy.cfg_reg = 0;
+		cs->subtyp = NICCY_PNP;
+		cs->irq = card->para[0];
+		if (check_region((cs->hw.niccy.isac), 2)) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 1);
+			return (0);
+		} else
+			request_region(cs->hw.niccy.isac, 2, "niccy data");
+		if (check_region((cs->hw.niccy.isac_ale), 2)) {
+			printk(KERN_WARNING
+				"HiSax: %s address port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac_ale,
+				cs->hw.niccy.isac_ale + 1);
+			release_region(cs->hw.niccy.isac, 2);
+			return (0);
+		} else
+			request_region(cs->hw.niccy.isac_ale, 2, "niccy addr");
+	} else {
+#if CONFIG_PCI
+		u_char pci_bus, pci_device_fn, pci_irq;
+		u_int pci_ioaddr;
+
+		cs->subtyp = 0;
+		for (; pci_index < 0xff; pci_index++) {
+			if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS,
+			   PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn)
+			   == PCIBIOS_SUCCESSFUL)
+				cs->subtyp = NICCY_PCI;
+			else
+				break;
+			/* get IRQ */
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &pci_irq);
+
+			/* get IO pci AMCC address */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			if (!pci_ioaddr) {
+				printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
+				return(0);
+			}
+			cs->hw.niccy.cfg_reg = pci_ioaddr & ~3 ;
+			/* get IO address */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_1, &pci_ioaddr);
+			if (cs->subtyp)
+				break;
+		}
+		if (!cs->subtyp) {
+			printk(KERN_WARNING "Niccy: No PCI card found\n");
+			return(0);
+		}
+		pci_index++;
+		if (!pci_irq) {
+			printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
+			return(0);
+		}
+		if (!pci_ioaddr) {
+			printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		
+		pci_ioaddr &= ~3; /* remove io/mem flag */
+		cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
+		cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
+		cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
+		cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
+		cs->irq = pci_irq;
+		if (check_region((cs->hw.niccy.isac), 4)) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 4);
+			return (0);
+		} else
+			request_region(cs->hw.niccy.isac, 4, "niccy");
+		if (check_region(cs->hw.niccy.cfg_reg, 0x80)) {
+			printk(KERN_WARNING
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.cfg_reg,
+				cs->hw.niccy.cfg_reg + 0x80);
+			release_region(cs->hw.niccy.isac, 4);
+			return (0);
+		} else {
+			request_region(cs->hw.niccy.cfg_reg, 0x80, "niccy pci");
+		}
+#else
+		printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+	printk(KERN_INFO
+		"HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n",
+		CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI",
+		cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &niccy_card_msg;
+	ISACVersion(cs, "Niccy:");
+	if (HscxVersion(cs, "Niccy:")) {
+		printk(KERN_WARNING
+		    "Niccy: wrong HSCX versions check IO address\n");
+		release_io_niccy(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c
--- v2.0.35/linux/drivers/isdn/hisax/q931.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/q931.c	Sun Nov 15 10:32:58 1998
@@ -1,4 +1,4 @@
-/* $Id: q931.c,v 1.5 1997/04/06 22:56:43 keil Exp $
+/* $Id: q931.c,v 1.5.2.3 1998/11/03 00:07:28 keil Exp $
 
  * q931.c       code to decode ITU Q.931 call control messages
  *
@@ -14,6 +14,19 @@
  *
  *
  * $Log: q931.c,v $
+ * Revision 1.5.2.3  1998/11/03 00:07:28  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.5.2.2  1998/10/25 18:16:32  fritz
+ * Replaced some read-only variables by defines.
+ *
+ * Revision 1.5.2.1  1997/10/17 22:14:20  keil
+ * update to last hisax version
+ *
+ * Revision 1.6  1997/07/27 21:09:44  keil
+ * move functions to isdnl3.c
+ *
  * Revision 1.5  1997/04/06 22:56:43  keil
  * Some cosmetic changes
  *
@@ -37,44 +50,6 @@
 #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)
 {
@@ -88,14 +63,6 @@
 	*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
  */
@@ -202,7 +169,7 @@
 	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
 };
 
-int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType));
+#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType))
 
 static
 struct MessageType mt_n1[] =
@@ -239,7 +206,7 @@
 	{MT_N1_STAT, "STATus"}
 };
 
-int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType));
+#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
 
 static struct MessageType fac_1tr6[] =
 {
@@ -263,9 +230,7 @@
 	{FAC_Rueckwechsel, "Rueckwechsel"},
 	{FAC_Umleitung, "Umleitung"}
 };
-int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType));
-
-
+#define FAC_1TR6_LEN (sizeof(fac_1tr6) / sizeof(struct MessageType))
 
 static int
 prbits(char *dest, u_char b, int start, int len)
@@ -968,7 +933,7 @@
 	{WE0_userInfo, "User Info", general}
 };
 
-static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement));
+#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement))
 
 static struct InformationElement we_6[] =
 {
@@ -980,7 +945,7 @@
 	{WE6_statusCalled, "Status Called", general},
 	{WE6_addTransAttr, "Additional Transmission Attributes", general}
 };
-static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement));
+#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement))
 
 int
 QuickHex(char *txt, u_char * p, int cnt)
@@ -1007,39 +972,92 @@
 }
 
 void
-LogFrame(struct IsdnCardState *sp, u_char * buf, int size)
+LogFrame(struct IsdnCardState *cs, u_char * buf, int size)
 {
 	char *dp;
 
 	if (size < 1)
 		return;
-	dp = sp->dlogspace;
-	if (size < 4096 / 3 - 10) {
-		dp += sprintf(dp, "HEX:");
+	dp = cs->dlog;
+	if (size < MAX_DLOG_SPACE / 3 - 10) {
+		*dp++ = 'H';
+		*dp++ = 'E';
+		*dp++ = 'X';
+		*dp++ = ':';
 		dp += QuickHex(dp, buf, size);
 		dp--;
 		*dp++ = '\n';
 		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
 	} else
-		sprintf(dp, "LogFrame: warning Frame too big (%d)\n",
-			size);
-	HiSax_putstatus(sp, sp->dlogspace);
+		HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
 }
 
 void
-dlogframe(struct IsdnCardState *sp, u_char * buf, int size, char *comment)
+dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
 {
-	u_char *bend = buf + size;
+	u_char *bend, *buf;
 	char *dp;
 	unsigned char pd, cr_l, cr, mt;
-	int i, cs = 0, cs_old = 0, cs_fest = 0;
+	unsigned char sapi, tei, ftyp;
+	int i, cset = 0, cs_old = 0, cs_fest = 0;
+	int size, finish = 0;
 
-	if (size < 1)
+	if (skb->len < 3)
 		return;
 	/* display header */
-	dp = sp->dlogspace;
-	dp += sprintf(dp, "%s\n", comment);
-
+	dp = cs->dlog;
+	dp += jiftime(dp, jiffies);
+	*dp++ = ' ';
+	sapi = skb->data[0] >> 2;
+	tei  = skb->data[1] >> 1;
+	ftyp = skb->data[2];
+	buf = skb->data;
+	dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
+	size = skb->len;
+	
+	if (tei == GROUP_TEI) {
+		if (sapi == CTRL_SAPI) { /* sapi 0 */
+			if (ftyp == 3) {
+				dp += sprintf(dp, "broadcast\n");
+				buf += 3;
+				size -= 3;
+			} else {
+				dp += sprintf(dp, "no UI broadcast\n");
+				finish = 1;
+			}
+		} else if (sapi == TEI_SAPI) {
+			dp += sprintf(dp, "tei managment\n");
+			finish = 1;
+		} else {
+			dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
+			finish = 1;
+		}
+	} else {
+		if (sapi == CTRL_SAPI) {
+			if (!(ftyp & 1)) { /* IFrame */
+				dp += sprintf(dp, "with tei %d\n", tei);
+				buf += 4;
+				size -= 4;
+			} else {
+				dp += sprintf(dp, "SFrame with tei %d\n", tei);
+				finish = 1;
+			}
+		} else {
+			dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
+			finish = 1;
+		}
+	}
+	bend = skb->data + skb->len;
+	if (buf >= bend) {
+		dp += sprintf(dp, "frame too short\n");
+		finish = 1;
+	}
+	if (finish) {
+		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+		return;
+	}
 	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
 		/* locate message type */
 		pd = *buf++;
@@ -1050,11 +1068,11 @@
 			cr = 0;
 		mt = *buf++;
 		if (pd == PROTO_DIS_N0) {	/* N0 */
-			for (i = 0; i < mt_n0_len; i++)
+			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)
+			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);
@@ -1063,11 +1081,11 @@
 					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 					      size, mt_n0[i].descr);
 		} else {	/* N1 */
-			for (i = 0; i < mt_n1_len; i++)
+			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)
+			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);
@@ -1084,8 +1102,8 @@
 				switch ((*buf >> 4) & 7) {
 					case 1:
 						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
-						cs_old = cs;
-						cs = *buf & 7;
+						cs_old = cset;
+						cset = *buf & 7;
 						cs_fest = *buf & 8;
 						break;
 					case 3:
@@ -1109,33 +1127,33 @@
 				continue;
 			}
 			/* No, locate it in the table */
-			if (cs == 0) {
-				for (i = 0; i < we_0_len; i++)
+			if (cset == 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) {
+				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++)
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			} else if (cset == 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) {
+				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]);
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 			} else
-				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 			/* Skip to next element */
 			if (cs_fest == 8) {
-				cs = cs_old;
+				cset = cs_old;
 				cs_old = 0;
 				cs_fest = 0;
 			}
@@ -1213,6 +1231,6 @@
 	} else {
 		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
 	}
-	dp += sprintf(dp, "\n");
-	HiSax_putstatus(sp, sp->dlogspace);
+	*dp = 0;
+	HiSax_putstatus(cs, NULL, cs->dlog);
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/s0box.c linux/drivers/isdn/hisax/s0box.c
--- v2.0.35/linux/drivers/isdn/hisax/s0box.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/s0box.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,277 @@
+/* $Id: s0box.c,v 1.1.2.1 1998/04/11 18:44:41 keil Exp $
+
+ * s0box.c      low level stuff for Creatix S0BOX
+ *
+ * Author       S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de)
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *s0box_revision = "$Revision: 1.1.2.1 $";
+
+static inline void
+writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)&0x7f,padr);
+	outb_p(0x16,padr+2);
+	outb_p(val,padr);
+	outb_p(0x17,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	restore_flags(flags);
+}
+
+static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf,
+			 0, 0, 0, 0, 0, 0, 0, 0,
+			 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ;
+
+static inline u_char
+readreg(unsigned int padr, signed int addr, u_char off) {
+	register u_char n1, n2;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)|0x80,padr);
+	outb_p(0x16,padr+2);
+	outb_p(0x17,padr+2);
+	n1 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x16,padr+2);
+	n2 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	restore_flags(flags);
+	return nibtab[n1] | (nibtab[n2] << 4);
+}
+
+static inline void
+read_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	register u_char n1, n2;
+	
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr|0x80, padr);
+	outb_p(0x16, padr+2);
+	for (i=0; i<size; i++) {
+		outb_p(0x17, padr+2);
+		n1 = (inb_p(padr+1) >> 3) & 0x17;
+		outb_p(0x16,padr+2);
+		n2 = (inb_p(padr+1) >> 3) & 0x17;
+		*(data++)=nibtab[n1] | (nibtab[n2] << 4);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+static inline void
+write_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr&0x7f, padr);
+	for (i=0; i<size; i++) {
+		outb_p(0x16, padr+2);
+		outb_p(*(data++), padr);
+		outb_p(0x17, padr+2);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+	int count = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	count++;
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (count >= MAXCOUNT)
+		printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count);
+	if (stat & 1) {
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_s0box(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.teles3.cfg_reg, 8);
+}
+
+static int
+S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			break;
+		case CARD_RELEASE:
+			release_io_s0box(cs);
+			break;
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &s0box_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			break;
+		case CARD_TEST:
+			break;
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_s0box(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, s0box_revision);
+	printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_S0BOX)
+		return (0);
+
+	cs->hw.teles3.cfg_reg = card->para[1];
+	cs->hw.teles3.hscx[0] = -0x20;
+	cs->hw.teles3.hscx[1] = 0x0;
+	cs->hw.teles3.isac = 0x20;
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	cs->irq = card->para[0];
+	if (check_region(cs->hw.teles3.cfg_reg,8)) {
+		printk(KERN_WARNING
+		       "HiSax: %s ports %x-%x already in use\n",
+		       CardType[cs->typ],
+                       cs->hw.teles3.cfg_reg,
+                       cs->hw.teles3.cfg_reg + 7);
+		return 0;
+	} else
+		request_region(cs->hw.teles3.cfg_reg, 8, "S0Box parallel I/O");
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d isac:0x%x  cfg:0x%x\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles3.isac, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%x  hscx B:0x%x\n",
+	       cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &S0Box_card_msg;
+	ISACVersion(cs, "S0Box:");
+	if (HscxVersion(cs, "S0Box:")) {
+		printk(KERN_WARNING
+		       "S0Box: wrong HSCX versions check IO address\n");
+		release_io_s0box(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/sedlbauer.c linux/drivers/isdn/hisax/sedlbauer.c
--- v2.0.35/linux/drivers/isdn/hisax/sedlbauer.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/sedlbauer.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,776 @@
+/* $Id: sedlbauer.c,v 1.1.2.16 1998/11/08 13:01:01 niemann Exp $
+
+ * sedlbauer.c  low level stuff for Sedlbauer cards
+ *              includes support for the Sedlbauer speed star (speed star II),
+ *              support for the Sedlbauer speed fax+,
+ *              support for the Sedlbauer ISDN-Controller PC/104 and
+ *              support for the Sedlbauer speed pci
+ *              derived from the original file asuscom.c from Karsten Keil
+ *
+ * Copyright (C) 1997,1998 Marcus Niemann (for the modifications to
+ *                                         the original file asuscom.c)
+ *
+ * Author     Marcus Niemann (niemann@www-bib.fh-bielefeld.de)
+ *
+ * Thanks to  Karsten Keil
+ *            Sedlbauer AG for informations
+ *            Edgar Toernig
+ *
+ * $Log: sedlbauer.c,v $
+ * Revision 1.1.2.16  1998/11/08 13:01:01  niemann
+ * Added doc for Sedlbauer ISDN cards,
+ * added info for downloading firmware (Sedlbauer speed fax+)
+ *
+ * Revision 1.1.2.15  1998/11/03 00:07:32  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.14  1998/10/30 22:51:41  niemann
+ * Added new card, Sedlbauer speed pci works now.
+ *
+ * Revision 1.1.2.13  1998/10/16 12:46:06  keil
+ * fix pci detection for more as one card
+ *
+ * Revision 1.1.2.12  1998/10/13 18:38:53  keil
+ * Fix PCI detection
+ *
+ * Revision 1.1.2.11  1998/10/13 10:27:30  keil
+ * New cards, minor fixes
+ *
+ * Revision 1.1.2.10  1998/10/11 19:33:52  niemann
+ * Added new IPAC based cards.
+ * Code cleanup and simplified (sedlbauer.c)
+ *
+ * Revision 1.1.2.9  1998/10/04 23:05:03  keil
+ * ISAR works now
+ *
+ * Revision 1.1.2.8  1998/09/30 22:28:10  keil
+ * more work for isar support
+ *
+ * Revision 1.1.2.7  1998/09/27 13:07:01  keil
+ * Apply most changes from 2.1.X (HiSax 3.1)
+ *
+ * Revision 1.1.2.6  1998/09/12 18:44:06  niemann
+ * Added new card: Sedlbauer ISDN-Controller PC/104
+ *
+ * Revision 1.1.2.5  1998/04/08 21:58:44  keil
+ * New init code
+ *
+ * Revision 1.1.2.4  1998/02/09 11:21:17  keil
+ * Sedlbauer PCMCIA support from Marcus Niemann
+ *
+ * Revision 1.1.2.3  1998/01/27 22:37:29  keil
+ * fast io
+ *
+ * Revision 1.1.2.2  1997/11/15 18:50:56  keil
+ * new common init function
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:56  keil
+ * new files on 2.0
+ *
+ * Revision 1.1  1997/09/11 17:32:04  keil
+ * new
+ *
+ *
+ */
+
+/* Supported cards:
+ * Card:	Chip:		Configuration:	Comment:
+ * ---------------------------------------------------------------------
+ * Speed Card	ISAC_HSCX	DIP-SWITCH
+ * Speed Win	ISAC_HSCX	ISAPNP
+ * Speed Fax+	ISAC_ISAR	ISAPNP		#HDLC works#
+ * Speed Star	ISAC_HSCX	CARDMGR
+ * Speed Win2	IPAC		ISAPNP
+ * ISDN PC/104	IPAC		DIP-SWITCH
+ * Speed Star2	IPAC		CARDMGR
+ * Speed PCI	IPAC		PNP		
+ *
+ * Important:
+ * For the sedlbauer speed fax+ to work properly you have to download 
+ * the firmware onto the card.
+ * For example: hisaxctrl <DriverID> 9 ISAR.BIN
+*/
+
+#define SEDLBAUER_PCI 1
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+extern const char *CardType[];
+
+const char *Sedlbauer_revision = "$Revision: 1.1.2.16 $";
+
+const char *Sedlbauer_Types[] =
+	{"None", "speed card/win", "speed star", "speed fax+", 
+	"speed win II / ISDN PC/104", "speed star II", "speed pci"};
+
+#ifdef SEDLBAUER_PCI
+#define PCI_VENDOR_SEDLBAUER	0xe159
+#define PCI_SPEEDPCI_ID	0x02
+#endif
+ 
+#define SEDL_SPEED_CARD_WIN	1
+#define SEDL_SPEED_STAR 	2
+#define SEDL_SPEED_FAX		3
+#define SEDL_SPEED_WIN2_PC104 	4
+#define SEDL_SPEED_STAR2 	5
+#define SEDL_SPEED_PCI   	6
+
+#define SEDL_CHIP_TEST		0
+#define SEDL_CHIP_ISAC_HSCX	1
+#define SEDL_CHIP_ISAC_ISAR	2
+#define SEDL_CHIP_IPAC		3
+
+#define SEDL_BUS_ISA		1
+#define SEDL_BUS_PCI		2
+#define	SEDL_BUS_PCMCIA		3
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SEDL_HSCX_ISA_RESET_ON	0
+#define SEDL_HSCX_ISA_RESET_OFF	1
+#define SEDL_HSCX_ISA_ISAC	2
+#define SEDL_HSCX_ISA_HSCX	3
+#define SEDL_HSCX_ISA_ADR	4
+
+#define SEDL_HSCX_PCMCIA_RESET	0
+#define SEDL_HSCX_PCMCIA_ISAC	1
+#define SEDL_HSCX_PCMCIA_HSCX	2
+#define SEDL_HSCX_PCMCIA_ADR	4
+
+#define SEDL_ISAR_ISA_ISAC		4
+#define SEDL_ISAR_ISA_ISAR		6
+#define SEDL_ISAR_ISA_ADR		8
+#define SEDL_ISAR_ISA_ISAR_RESET_ON	10
+#define SEDL_ISAR_ISA_ISAR_RESET_OFF	12
+
+#define SEDL_IPAC_ANY_ADR 	0
+#define SEDL_IPAC_ANY_IPAC	2
+
+#define SEDL_IPAC_PCI_BASE	0
+#define SEDL_IPAC_PCI_ADR	0xc0
+#define SEDL_IPAC_PCI_IPAC	0xc8
+
+#define SEDL_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+        return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+        writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+        readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+        writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr,
+			cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr,
+		 cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* ISAR access routines
+ * mode = 0 access with IRQ on
+ * mode = 1 access with IRQ off
+ * mode = 2 access with IRQ off and using last offset
+ */
+  
+static u_char
+ReadISAR(struct IsdnCardState *cs, int mode, u_char offset)
+{	
+	if (mode == 0)
+		return (readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset));
+	else if (mode == 1)
+		byteout(cs->hw.sedl.adr, offset);
+	return(bytein(cs->hw.sedl.hscx));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+	if (mode == 0)
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset, value);
+	else {
+		if (mode == 1)
+			byteout(cs->hw.sedl.adr, offset);
+		byteout(cs->hw.sedl.hscx, value);
+	}
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+		return;
+	}
+
+	if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) {
+          /* The card tends to generate interrupts while being removed
+             causing us to just crash the kernel. bad. */
+          printk(KERN_WARNING "Sedlbauer: card not available!\n");
+          return;
+        }
+
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0);
+	}
+	if (stat & 2) {
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 20;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+		return;
+	}
+	ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0);
+}
+
+static void
+sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	int cnt = 20;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+		return;
+	}
+
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+      Start_ISAR:
+	if (val & ISAR_IRQSTA)
+		isar_int_main(cs);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+	if ((val & ISAR_IRQSTA) && --cnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "ISAR IntStat after IntRoutine");
+		goto Start_ISAR;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val && --cnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, ISAR_IRQMSK);
+}
+
+void
+release_io_sedlbauer(struct IsdnCardState *cs)
+{
+	int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8;
+
+	if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+		bytecnt = 256;
+	}
+	if (cs->hw.sedl.cfg_reg)
+		release_region(cs->hw.sedl.cfg_reg, bytecnt);
+}
+
+static void
+reset_sedlbauer(struct IsdnCardState *cs)
+{
+	long flags;
+
+	printk(KERN_INFO "Sedlbauer: resetting card\n");
+
+	if (!((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) &&
+	   (cs->hw.sedl.chip == SEDL_CHIP_ISAC_HSCX))) {
+		if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
+			save_flags(flags);
+			sti();
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12);
+			restore_flags(flags);
+		} else {		
+			byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
+			save_flags(flags);
+			sti();
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+			byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+			restore_flags(flags);
+		}
+	}
+}
+
+static int
+Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_sedlbauer(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_sedlbauer(cs);
+			return(0);
+		case CARD_SETIRQ:
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				return(request_irq(cs->irq, &sedlbauer_interrupt_isar,
+					I4L_IRQ_FLAG, "HiSax", cs));
+			} else if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+				return(request_irq(cs->irq, &sedlbauer_interrupt_ipac,
+					I4L_IRQ_FLAG, "HiSax", cs));
+			} else {
+				return(request_irq(cs->irq, &sedlbauer_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+			}
+		case CARD_INIT:
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				clear_pending_isac_ints(cs);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				initisac(cs);
+				initisar(cs);
+				/* Reenable all IRQ */
+				cs->writeisac(cs, ISAC_MASK, 0);
+				/* RESET Receiver and Transmitter */
+				cs->writeisac(cs, ISAC_CMDR, 0x41);
+			} else {
+				inithscxisac(cs, 3);
+			}
+			return(0);
+		case CARD_TEST:
+			return(0);
+		case CARD_LOAD_FIRM:
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				if (isar_load_firmware(cs, arg))
+					return(1);
+				else 
+					ll_run(cs);
+			}
+			return(0);
+	}
+	return(0);
+}
+
+
+#ifdef SEDLBAUER_PCI
+static  int pci_index __initdata = 0;
+#endif
+
+__initfunc(int
+setup_sedlbauer(struct IsdnCard *card))
+{
+	int bytecnt, ver, val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Sedlbauer_revision);
+	printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
+	
+ 	if (cs->typ == ISDN_CTYPE_SEDLBAUER) {
+ 		cs->subtyp = SEDL_SPEED_CARD_WIN;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) {	
+ 		cs->subtyp = SEDL_SPEED_STAR;
+		cs->hw.sedl.bus = SEDL_BUS_PCMCIA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_FAX) {	
+ 		cs->subtyp = SEDL_SPEED_FAX;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+ 	} else
+		return (0);
+
+	bytecnt = 8;
+	if (card->para[1]) {
+		cs->hw.sedl.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			bytecnt = 16;
+		}
+	} else {
+/* Probe for Sedlbauer speed pci */
+#if SEDLBAUER_PCI
+#if CONFIG_PCI
+		for (; pci_index < 255; pci_index++) {
+			unsigned char pci_bus, pci_device_fn;
+			unsigned int ioaddr;
+			unsigned char irq;
+
+			if (pcibios_find_device (PCI_VENDOR_SEDLBAUER,
+						PCI_SPEEDPCI_ID, 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_0, &ioaddr);
+			cs->irq = irq;
+			cs->hw.sedl.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK; 
+			if (!cs->hw.sedl.cfg_reg) {
+				printk(KERN_WARNING "Sedlbauer: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			cs->hw.sedl.bus = SEDL_BUS_PCI;
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+			cs->subtyp = SEDL_SPEED_PCI;
+			bytecnt = 256;
+			byteout(cs->hw.sedl.cfg_reg, 0xff);
+			byteout(cs->hw.sedl.cfg_reg, 0x00);
+			byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd);
+			byteout(cs->hw.sedl.cfg_reg+ 5, 0x02);
+			break;
+		}	
+		if (pci_index == 255) {
+			printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
+			return(0);
+		}
+		pci_index++;
+#else
+		printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
+		return (0);
+#endif /* CONFIG_PCI */
+#endif /* SEDLBAUER_PCI */
+	}	
+	
+       	/* In case of the sedlbauer pcmcia card, this region is in use,
+           reserved for us by the card manager. So we do not check it
+           here, it would fail. */
+	if (cs->hw.sedl.bus != SEDL_BUS_PCMCIA &&
+		check_region((cs->hw.sedl.cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+			"HiSax: %s config port %x-%x already in use\n",
+			CardType[card->typ],
+			cs->hw.sedl.cfg_reg,
+			cs->hw.sedl.cfg_reg + bytecnt);
+			return (0);
+	} else {
+		request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn");
+	}
+
+	printk(KERN_INFO
+	       "Sedlbauer: defined at 0x%x-0x%x IRQ %d\n",
+	       cs->hw.sedl.cfg_reg,
+	       cs->hw.sedl.cfg_reg + bytecnt,
+	       cs->irq);
+
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sedl_card_msg;
+
+/*
+ * testing ISA and PCMCIA Cards for IPAC, default is ISAC 
+ * do not test for PCI card, because ports are different
+ * and PCI card uses only IPAC (for the moment)
+ */	
+	if (cs->hw.sedl.bus != SEDL_BUS_PCI) {
+		val = readreg(cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR,
+        	        cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC, IPAC_ID);
+	        if (val == 1) {
+		/* IPAC */
+                	cs->subtyp = SEDL_SPEED_WIN2_PC104;
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->subtyp = SEDL_SPEED_STAR2;
+			}
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+		} else {
+		/* ISAC_HSCX oder ISAC_ISAR */
+			if (cs->hw.sedl.chip == SEDL_CHIP_TEST) {
+				cs->hw.sedl.chip = SEDL_CHIP_ISAC_HSCX;
+			}
+		}
+	}
+
+/*
+ * hw.sedl.chip is now properly set
+ */
+	printk(KERN_INFO "Sedlbauer: %s detected\n",
+		Sedlbauer_Types[cs->subtyp]);
+
+
+	if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+	/* IPAC */
+		if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR;
+        	        cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+                	cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+		} else {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR;
+        	        cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+                	cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+		}
+                test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+                cs->readisac = &ReadISAC_IPAC;
+                cs->writeisac = &WriteISAC_IPAC;
+                cs->readisacfifo = &ReadISACfifo_IPAC;
+                cs->writeisacfifo = &WriteISACfifo_IPAC;
+
+		val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID);
+                printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val);
+		reset_sedlbauer(cs);
+	} else {
+	/* ISAC_HSCX oder ISAC_ISAR */
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ADR;
+			cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAC;
+			cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR;
+			cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_ON;
+			cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_OFF;
+			cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar;
+			cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar;
+			test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+	
+			ISACVersion(cs, "Sedlbauer:");
+		
+			cs->BC_Read_Reg = &ReadISAR;
+			cs->BC_Write_Reg = &WriteISAR;
+			cs->BC_Send_Data = &isar_fill_fifo;
+			ver = ISARVersion(cs, "Sedlbauer:");
+			if (ver < 0) {
+				printk(KERN_WARNING
+					"Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+		} else {
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+			} else {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF;
+			}
+			ISACVersion(cs, "Sedlbauer:");
+		
+			if (HscxVersion(cs, "Sedlbauer:")) {
+				printk(KERN_WARNING
+					"Sedlbauer: wrong HSCX versions check IO address\n");
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+			reset_sedlbauer(cs);
+		}
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/sportster.c linux/drivers/isdn/hisax/sportster.c
--- v2.0.35/linux/drivers/isdn/hisax/sportster.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/sportster.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,286 @@
+/* $Id: sportster.c,v 1.1.2.4 1998/04/08 21:58:46 keil Exp $
+
+ * sportster.c     low level stuff for USR Sportster internal TA
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
+ *
+ * $Log: sportster.c,v $
+ * Revision 1.1.2.4  1998/04/08 21:58:46  keil
+ * New init code
+ *
+ * Revision 1.1.2.3  1998/01/27 22:37:31  keil
+ * fast io
+ *
+ * Revision 1.1.2.2  1997/11/15 18:50:57  keil
+ * new common init function
+ *
+ * Revision 1.1.2.1  1997/10/17 22:10:58  keil
+ * new files on 2.0
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *sportster_revision = "$Revision: 1.1.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define	 SPORTSTER_ISAC		0xC000
+#define	 SPORTSTER_HSCXA	0x0000
+#define	 SPORTSTER_HSCXB	0x4000
+#define	 SPORTSTER_RES_IRQ	0x8000
+#define	 SPORTSTER_RESET	0x80
+#define	 SPORTSTER_INTE		0x40
+
+static inline int
+calc_off(unsigned int base, unsigned int off)
+{
+	return(base + ((off & 0xfc)<<8) + ((off & 3)<<1));
+}
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.isac, offset)));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.isac, offset), value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.spt.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.spt.isac, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg))
+#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+
+	if (!cs) {
+		printk(KERN_WARNING "Sportster: Spurious interrupt!\n");
+		return;
+	}
+	val = READHSCX(cs, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = ReadISAC(cs, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = READHSCX(cs, 1, HSCX_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = ReadISAC(cs, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	/* get a new irq impulse if there any pending */
+	bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1);
+}
+
+void
+release_io_sportster(struct IsdnCardState *cs)
+{
+	int i, adr;
+
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0);
+	for (i=0; i<64; i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		release_region(adr, 8);
+	}
+}
+
+void
+reset_sportster(struct IsdnCardState *cs)
+{
+	long flags;
+
+	cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	restore_flags(flags);
+}
+
+static int
+Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_sportster(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_sportster(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &sportster_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 1);
+			cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
+			byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+			inithscxisac(cs, 2);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+get_io_range(struct IsdnCardState *cs))
+{
+	int i, j, adr;
+	
+	for (i=0;i<64;i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		if (check_region(adr, 8)) {
+			printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				CardType[cs->typ], adr, adr + 8);
+			break;
+		} else
+			request_region(adr, 8, "sportster");
+	}
+	if (i==64)
+		return(1);
+	else {
+		for (j=0; j<i; j++) {
+			adr = cs->hw.spt.cfg_reg + j *1024;
+			release_region(adr, 8);
+		}
+		return(0);
+	}
+}
+
+__initfunc(int
+setup_sportster(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, sportster_revision);
+	printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_SPORTSTER)
+		return (0);
+
+	cs->hw.spt.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (!get_io_range(cs))
+		return (0);
+	cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC;
+	cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA;
+	cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB;
+	
+	switch(cs->irq) {
+		case 5:	cs->hw.spt.res_irq = 1;
+			break;
+		case 7:	cs->hw.spt.res_irq = 2;
+			break;
+		case 10:cs->hw.spt.res_irq = 3;
+			break;
+		case 11:cs->hw.spt.res_irq = 4;
+			break;
+		case 12:cs->hw.spt.res_irq = 5;
+			break;
+		case 14:cs->hw.spt.res_irq = 6;
+			break;
+		case 15:cs->hw.spt.res_irq = 7;
+			break;
+		default:release_io_sportster(cs);
+			printk(KERN_WARNING "Sportster: wrong IRQ\n");
+			return(0);
+	}
+	reset_sportster(cs);
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.spt.cfg_reg);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sportster_card_msg;
+	ISACVersion(cs, "Sportster:");
+	if (HscxVersion(cs, "Sportster:")) {
+		printk(KERN_WARNING
+		       "Sportster: wrong HSCX versions check IO address\n");
+		release_io_sportster(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c
--- v2.0.35/linux/drivers/isdn/hisax/tei.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/hisax/tei.c	Sun Nov 15 10:32:58 1998
@@ -1,58 +1,124 @@
-/* $Id: tei.c,v 1.8 1997/04/07 22:59:08 keil Exp $
+/* $Id: tei.c,v 1.8.2.7 1998/11/03 00:07:35 keil Exp $
 
- * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author       Karsten Keil (keil@isdn4linux.de)
  *              based on the teles driver from Jan den Ouden
  *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/HiSax.cert
+ *
  * 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.8.2.7  1998/11/03 00:07:35  keil
+ * certification related changes
+ * fixed logging for smaller stack use
  *
- * Revision 1.7  1997/04/06 22:54:03  keil
- * Using SKB's
+ * Revision 1.8.2.6  1998/05/27 18:06:21  keil
+ * HiSax 3.0
+ *
+ * Revision 1.8.2.5  1998/03/07 23:15:38  tsbogend
+ * made HiSax working on Linux/Alpha
  *
- * Revision 1.6  1997/02/09 00:25:12  keil
- * new interface handling, one interface per card
+ * Revision 1.8.2.4  1998/01/27 22:43:49  keil
+ * fixed MDL_ASSIGN_REQ
  *
- * Revision 1.5  1997/01/27 15:57:51  keil
+ * Revision 1.8.2.3  1997/11/15 18:54:20  keil
  * cosmetics
  *
- * Revision 1.4  1997/01/21 22:32:44  keil
- * Tei verify request
+ * Revision 1.8.2.2  1997/10/17 22:14:23  keil
+ * update to last hisax version
+ *
+ * Revision 2.2  1997/07/31 19:24:39  keil
+ * fixed a warning
  *
- * Revision 1.3  1997/01/04 13:45:02  keil
- * cleanup,adding remove tei request (thanks to Sim Yskes)
+ * Revision 2.1  1997/07/31 11:50:16  keil
+ * ONE TEI and FIXED TEI handling
  *
- * Revision 1.2  1996/12/08 19:52:39  keil
- * minor debug fix
+ * Revision 2.0  1997/07/27 21:13:30  keil
+ * New TEI managment
  *
- * Revision 1.1  1996/10/13 20:04:57  keil
- * Initial revision
+ * Revision 1.9  1997/06/26 11:18:02  keil
+ * New managment
  *
+ * 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
+ *
+ * Old log removed/ KKe
  *
  */
 #define __NO_VERSION__
 #include "hisax.h"
+#include "isdnl2.h"
+#include <linux/random.h>
 
-extern struct IsdnCard cards[];
-extern int nrcards;
+const char *tei_revision = "$Revision: 1.8.2.7 $";
 
-const char *tei_revision = "$Revision: 1.8 $";
+#define ID_REQUEST	1
+#define ID_ASSIGNED	2
+#define ID_DENIED	3
+#define ID_CHK_REQ	4
+#define ID_CHK_RES	5
+#define ID_REMOVE	6
+#define ID_VERIFY	7
+
+#define TEI_ENTITY_ID	0xf
+
+static
+struct Fsm teifsm =
+{NULL, 0, 0, NULL, NULL};
+
+void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb);
+
+enum {
+	ST_TEI_NOP,
+	ST_TEI_IDREQ,
+	ST_TEI_IDVERIFY,
+};
+
+#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1)
+
+static char *strTeiState[] =
+{
+	"ST_TEI_NOP",
+	"ST_TEI_IDREQ",
+	"ST_TEI_IDVERIFY",
+};
+
+enum {
+	EV_IDREQ,
+	EV_ASSIGN,
+	EV_DENIED,
+	EV_CHKREQ,
+	EV_REMOVE,
+	EV_VERIFY,
+	EV_T202,
+};
+
+#define TEI_EVENT_COUNT (EV_T202+1)
+
+static char *strTeiEvent[] =
+{
+	"EV_IDREQ",
+	"EV_ASSIGN",
+	"EV_DENIED",
+	"EV_CHKREQ",
+	"EV_REMOVE",
+	"EV_VERIFY",
+	"EV_T202",
+};
 
-static struct PStack *
-findces(struct PStack *st, int ces)
+unsigned int
+random_ri(void)
 {
-	struct PStack *ptr = *(st->l1.stlistp);
+	unsigned int x;
 
-	while (ptr)
-		if (ptr->l2.ces == ces)
-			return (ptr);
-		else
-			ptr = ptr->next;
-	return (NULL);
+	get_random_bytes(&x, sizeof(x));
+	return (x & 0xffff);
 }
 
 static struct PStack *
@@ -72,246 +138,346 @@
 }
 
 static void
-mdl_unit_data_res(struct PStack *st, unsigned int ri, u_char mt, u_char ai)
+put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei)
 {
 	struct sk_buff *skb;
 	u_char *bp;
 
-	if (!(skb = alloc_skb(6 + MAX_HEADER_LEN, GFP_ATOMIC))) {
+	if (!(skb = alloc_skb(8, 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, 3);
+	bp[0] = (TEI_SAPI << 2);
+	bp[1] = (GROUP_TEI << 1) | 0x1;
+	bp[2] = UI;
 	bp = skb_put(skb, 5);
-	bp[0] = 0xf;
+	bp[0] = TEI_ENTITY_ID;
 	bp[1] = ri >> 8;
 	bp[2] = ri & 0xff;
-	bp[3] = mt;
-	bp[4] = (ai << 1) | 1;
-	st->l3.l3l2(st, DL_UNIT_DATA, skb);
+	bp[3] = m_id;
+	bp[4] = (tei << 1) | 1;
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
 }
 
 static void
-mdl_unit_data_ind(struct PStack *st, unsigned int ri, u_char mt, u_char ai)
+tei_id_request(struct FsmInst *fi, int event, void *arg)
 {
-	unsigned int tces;
-	struct PStack *otsp, *ptr;
-	char tmp[64];
+	struct PStack *st = fi->userdata;
 
-	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);
-			}
+	if (st->l2.tei != -1) {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request for allready asigned tei %d",
+			st->l2.tei);
+		return;
 	}
+	st->ma.ri = random_ri();
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request ri %d", st->ma.ri);
+	put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1);
+	st->ma.N202 = 3;
 }
 
-void
-tei_handler(struct PStack *st,
-	    u_char pr, struct sk_buff *skb)
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
 {
-	u_char *bp;
-	unsigned int data;
-	char tmp[32];
+	struct PStack *ost, *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int ri, tei;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity assign ri %d tei %d", ri, tei);
+	if ((ost = findtei(st, tei))) {		/* same tei is in use */
+		if (ri != ost->ma.ri) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"possible duplicate assignment tei %d", tei);
+			ost->l2.l2tei(ost, MDL_ERROR | RESPONSE, NULL);
+		}
+	} else if (ri == st->ma.ri) {
+		FsmDelTimer(&st->ma.t202, 1);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+	}
+}
 
-	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;
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int ri, tei;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity denied ri %d tei %d", ri, tei);
+}
+
+static void
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int tei;
+
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity check req tei %d", tei);
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 4);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei);
 	}
 }
 
-unsigned int
-randomces(void)
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int tei;
+
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity remove tei %d", tei);
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 5);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+	}
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
 {
-	int x = jiffies & 0xffff;
+	struct PStack *st = fi->userdata;
 
-	return (x);
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"id verify request for tei %d", st->l2.tei);
+	put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2);
+	st->ma.N202 = 2;
 }
 
 static void
-tei_man(struct PStack *sp, int i, void *v)
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
 {
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		st->ma.ri = random_ri();
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"assign req(%d) ri %d", 4 - st->ma.N202,
+				st->ma.ri);
+		put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
+		st->l3.l3l2(st, MDL_ERROR | RESPONSE, 0);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"id verify req(%d) for tei %d",
+				3 - st->ma.N202, st->l2.tei);
+		put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4);
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"verify req for tei %d failed", st->l2.tei);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int mt;
+
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		dev_kfree_skb(skb, FREE_READ);
+		return;
+	}
 
-	printk(KERN_DEBUG "tei_man\n");
+	if (pr == (PH_DATA | INDICATION)) {
+		if (skb->len < 3) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"short mgr frame %ld/3", skb->len);
+		} else if (((skb->data[0] >> 2) != TEI_SAPI) ||
+			   ((skb->data[1] >> 1) != GROUP_TEI)) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"wrong mgr sapi/tei %x/%x",
+				skb->data[0], skb->data[1]);
+		} else if ((skb->data[2] & 0xef) != UI) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"mgr frame is not ui %x", skb->data[2]);
+		} else {
+			skb_pull(skb, 3);
+			if (skb->len < 5) {
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"short mgr frame %ld/5", skb->len);
+			} else if (skb->data[0] != TEI_ENTITY_ID) {
+				/* wrong management entity identifier, ignore */
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"tei handler wrong entity id %x",
+					skb->data[0]);
+			} else {
+				mt = skb->data[3];
+				if (mt == ID_ASSIGNED)
+					FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb);
+				else if (mt == ID_DENIED)
+					FsmEvent(&st->ma.tei_m, EV_DENIED, skb);
+				else if (mt == ID_CHK_REQ)
+					FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb);
+				else if (mt == ID_REMOVE)
+					FsmEvent(&st->ma.tei_m, EV_REMOVE, skb);
+				else {
+					st->ma.tei_m.printdebug(&st->ma.tei_m,
+						"tei handler wrong mt %x\n", mt);
+				}
+			}
+		}
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"tei handler wrong pr %x\n", pr);
+	}
+	dev_kfree_skb(skb, FREE_READ);
 }
 
 static void
 tei_l2tei(struct PStack *st, int pr, void *arg)
 {
-	struct IsdnCardState *sp = st->l1.hardware;
+	struct IsdnCardState *cs;
+
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		if (pr == (MDL_ASSIGN | INDICATION)) {
+			if (st->ma.debug)
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"fixed assign tei %d", st->l2.tei);
+			st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+			cs = (struct IsdnCardState *) st->l1.hardware;
+			cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+		}
+		return;
+	}
+	switch (pr) {
+		case (MDL_ASSIGN | INDICATION):
+			FsmEvent(&st->ma.tei_m, EV_IDREQ, arg);
+			break;
+		case (MDL_ERROR | REQUEST):
+			FsmEvent(&st->ma.tei_m, EV_VERIFY, arg);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
 
-	tei_handler(sp->teistack, pr, arg);
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, "tei ", fmt, args);
+	va_end(args);
 }
 
 void
 setstack_tei(struct PStack *st)
 {
 	st->l2.l2tei = tei_l2tei;
+	st->ma.T202 = 2000;	/* T202  2000 milliseconds */
+	st->l1.l1tei = tei_l1l2;
+	st->ma.debug = 1;
+	st->ma.tei_m.fsm = &teifsm;
+	st->ma.tei_m.state = ST_TEI_NOP;
+	st->ma.tei_m.debug = 1;
+	st->ma.tei_m.userdata = st;
+	st->ma.tei_m.userint = 0;
+	st->ma.tei_m.printdebug = tei_debug;
+	FsmInitTimer(&st->ma.tei_m, &st->ma.t202);
 }
 
 void
-init_tei(struct IsdnCardState *sp, int protocol)
+init_tei(struct IsdnCardState *cs, 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;
+void
+release_tei(struct IsdnCardState *cs)
+{
+	struct PStack *st = cs->stlist;
 
-	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;
+	while (st) {
+		FsmDelTimer(&st->ma.t202, 1);
+		st = st->next;
+	}
+}
 
-	HiSax_addlist(sp, st);
-	sp->teistack = st;
+static struct FsmNode TeiFnList[] HISAX_INITDATA =
+{
+	{ST_TEI_NOP, EV_IDREQ, tei_id_request},
+	{ST_TEI_NOP, EV_VERIFY, tei_id_verify},
+	{ST_TEI_NOP, EV_REMOVE, tei_id_remove},
+	{ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req},
+	{ST_TEI_IDREQ, EV_T202, tei_id_req_tout},
+	{ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
+	{ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
+	{ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout},
+	{ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
+	{ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
+};
+
+#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode))
+
+HISAX_INITFUNC(void
+TeiNew(void))
+{
+	teifsm.state_count = TEI_STATE_COUNT;
+	teifsm.event_count = TEI_EVENT_COUNT;
+	teifsm.strEvent = strTeiEvent;
+	teifsm.strState = strTeiState;
+	FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
 }
 
 void
-release_tei(struct IsdnCardState *sp)
+TeiFree(void)
 {
-	struct PStack *st = sp->teistack;
-
-	HiSax_rmlist(sp, st);
-	kfree((void *) st);
+	FsmFree(&teifsm);
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/teleint.c linux/drivers/isdn/hisax/teleint.c
--- v2.0.35/linux/drivers/isdn/hisax/teleint.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teleint.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,376 @@
+/* $Id: teleint.c,v 1.1.2.7 1998/11/03 00:07:39 keil Exp $
+
+ * teleint.c     low level stuff for TeleInt isdn cards
+ *
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: teleint.c,v $
+ * Revision 1.1.2.7  1998/11/03 00:07:39  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.6  1998/05/27 18:06:24  keil
+ * HiSax 3.0
+ *
+ * Revision 1.1.2.5  1998/04/08 21:58:48  keil
+ * New init code
+ *
+ * Revision 1.1.2.4  1998/04/04 21:58:27  keil
+ * fix HFC BUSY on ISAC fifos
+ *
+ * Revision 1.1.2.3  1998/01/27 22:37:41  keil
+ * fast io
+ *
+ * Revision 1.1.2.2  1997/11/15 18:50:58  keil
+ * new common init function
+ *
+ * Revision 1.1.2.1  1997/10/17 22:11:00  keil
+ * new files on 2.0
+ *
+ * Revision 1.1  1997/09/11 17:32:32  keil
+ * new
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hfc_2bs0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *TeleInt_revision = "$Revision: 1.1.2.7 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	int max_delay = 2000;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+		restore_flags(flags);
+		return (0);
+	}
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	register int max_delay = 20000;
+	register int i;
+	
+	byteout(ale, off);
+	for (i = 0; i<size; i++) {
+		ret = HFC_BUSY & bytein(ale);
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+			return;
+		}
+		data[i] = bytein(adr);
+	}
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	register u_char ret;
+	int max_delay = 2000;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+		restore_flags(flags);
+		return;
+	}
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	register int max_delay = 20000;
+	register int i;
+	
+	/* fifo write without cli because it's allready done  */
+	byteout(ale, off);
+	for (i = 0; i<size; i++) {
+		ret = HFC_BUSY & bytein(ale);
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+			return;
+		}
+		byteout(adr, data[i]);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	cs->hw.hfc.cip = offset;
+	return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	cs->hw.hfc.cip = offset;
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static u_char
+ReadHFC(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		cs->hw.hfc.cip = reg;
+		byteout(cs->hw.hfc.addr | 1, reg);
+		ret = bytein(cs->hw.hfc.addr);
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "hfc RD %02x %02x", reg, ret);
+	} else
+		ret = bytein(cs->hw.hfc.addr | 1);
+	return (ret);
+}
+
+static void
+WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	byteout(cs->hw.hfc.addr | 1, reg);
+	cs->hw.hfc.cip = reg;
+	if (data)
+		byteout(cs->hw.hfc.addr, value);
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+		debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
+}
+
+static void
+TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "TeleInt: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 2) {
+		writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF);
+		writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0);
+	}
+}
+
+static void
+TeleInt_Timer(struct IsdnCardState *cs)
+{
+	int stat = 0;
+
+	if (cs->bcs[0].mode) {
+		stat |= 1;
+		main_irq_hfc(&cs->bcs[0]);
+	}
+	if (cs->bcs[1].mode) {
+		stat |= 2;
+		main_irq_hfc(&cs->bcs[1]);
+	}
+	cs->hw.hfc.timer.expires = jiffies + 1;
+	add_timer(&cs->hw.hfc.timer);
+}
+
+void
+release_io_TeleInt(struct IsdnCardState *cs)
+{
+	del_timer(&cs->hw.hfc.timer);
+	releasehfc(cs);
+	if (cs->hw.hfc.addr)
+		release_region(cs->hw.hfc.addr, 2);
+}
+
+static void
+reset_TeleInt(struct IsdnCardState *cs)
+{
+	long flags;
+
+	printk(KERN_INFO "TeleInt: resetting card\n");
+	cs->hw.hfc.cirm |= HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset On */
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 3;
+	schedule();
+	cs->hw.hfc.cirm &= ~HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset Off */
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	restore_flags(flags);
+}
+
+static int
+TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_TeleInt(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_TeleInt(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &TeleInt_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithfc(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
+			cs->hw.hfc.timer.expires = jiffies + 1;
+			add_timer(&cs->hw.hfc.timer);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_TeleInt(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, TeleInt_revision);
+	printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELEINT)
+		return (0);
+
+	cs->hw.hfc.addr = card->para[1] & 0x3fe;
+	cs->irq = card->para[0];
+	cs->hw.hfc.cirm = HFC_CIRM;
+	cs->hw.hfc.isac_spcr = 0x00;
+	cs->hw.hfc.cip = 0;
+	cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfc.fifosize = 7 * 1024 + 512;
+	cs->hw.hfc.timer.function = (void *) TeleInt_Timer;
+	cs->hw.hfc.timer.data = (long) cs;
+	init_timer(&cs->hw.hfc.timer);
+	if (check_region((cs->hw.hfc.addr), 2)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfc.addr,
+		       cs->hw.hfc.addr + 2);
+		return (0);
+	} else {
+		request_region(cs->hw.hfc.addr, 2, "TeleInt isdn");
+	}
+	/* HW IO = IO */
+	byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff);
+	byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54);
+	switch (cs->irq) {
+		case 3:
+			cs->hw.hfc.cirm |= HFC_INTA;
+			break;
+		case 4:
+			cs->hw.hfc.cirm |= HFC_INTB;
+			break;
+		case 5:
+			cs->hw.hfc.cirm |= HFC_INTC;
+			break;
+		case 7:
+			cs->hw.hfc.cirm |= HFC_INTD;
+			break;
+		case 10:
+			cs->hw.hfc.cirm |= HFC_INTE;
+			break;
+		case 11:
+			cs->hw.hfc.cirm |= HFC_INTF;
+			break;
+		default:
+			printk(KERN_WARNING "TeleInt: wrong IRQ\n");
+			release_io_TeleInt(cs);
+			return (0);
+	}
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt);
+
+	printk(KERN_INFO
+	       "TeleInt: defined at 0x%x IRQ %d\n",
+	       cs->hw.hfc.addr,
+	       cs->irq);
+
+	reset_TeleInt(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHFC;
+	cs->BC_Write_Reg = &WriteHFC;
+	cs->cardmsg = &TeleInt_card_msg;
+	ISACVersion(cs, "TeleInt:");
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c
--- v2.0.35/linux/drivers/isdn/hisax/teles0.c	Tue Aug  5 09:00:12 1997
+++ linux/drivers/isdn/hisax/teles0.c	Sun Nov 15 10:32:58 1998
@@ -1,4 +1,4 @@
-/* $Id: teles0.c,v 1.8 1997/04/13 19:54:04 keil Exp $
+/* $Id: teles0.c,v 1.8.2.9 1998/04/08 21:58:49 keil Exp $
 
  * teles0.c     low level stuff for Teles Memory IO isdn cards
  *              based on the teles driver from Jan den Ouden
@@ -10,97 +10,103 @@
  *              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.8.2.9  1998/04/08 21:58:49  keil
+ * New init code
  *
- * Revision 1.7  1997/04/06 22:54:04  keil
- * Using SKB's
+ * Revision 1.8.2.8  1998/03/07 23:15:40  tsbogend
+ * made HiSax working on Linux/Alpha
  *
- * Revision 1.6  1997/01/27 15:52:18  keil
- * SMP proof,cosmetics
+ * Revision 1.8.2.7  1998/02/03 23:17:16  keil
+ * IRQ 9
  *
- * Revision 1.5  1997/01/21 22:25:59  keil
- * cleanups
+ * Revision 1.8.2.6  1998/01/27 22:37:43  keil
+ * fast io
  *
- * Revision 1.4  1996/11/05 19:41:27  keil
- * more changes for 2.1
+ * Revision 1.8.2.5  1997/11/15 18:51:00  keil
+ * new common init function
  *
- * Revision 1.3  1996/10/30 10:22:58  keil
- * Changes for 2.1 kernels
+ * Revision 1.8.2.4  1997/10/17 22:14:26  keil
+ * update to last hisax version
  *
- * Revision 1.2  1996/10/27 22:08:34  keil
- * cosmetic changes
+ * Revision 2.1  1997/07/27 21:47:10  keil
+ * new interface structures
  *
- * Revision 1.1  1996/10/13 20:04:58  keil
- * Initial revision
+ * Revision 2.0  1997/06/26 11:02:43  keil
+ * New Layer and card interface
  *
+ * 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
+ *
+ * removed old log info /KKe
  *
  */
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "teles0.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
+#include "isac.h"
+#include "hscx.h"
 
 extern const char *CardType[];
 
-const char *teles0_revision = "$Revision: 1.8 $";
+const char *teles0_revision = "$Revision: 1.8.2.9 $";
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 static inline u_char
 readisac(unsigned int adr, u_char off)
 {
-	return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+	return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off);
 }
 
 static inline void
 writeisac(unsigned int adr, u_char off, u_char data)
 {
-	writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
 }
 
 
 static inline u_char
 readhscx(unsigned int adr, int hscx, u_char off)
 {
-	return readb(adr + (hscx ? 0x1e0 : 0x1a0) +
+	return readb(adr + (hscx ? 0x1c0 : 0x180) +
 		     ((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);
+	writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
+	       ((off & 1) ? 0x1ff : 0) + off); mb();
 }
 
 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);
+	register u_char *ad = (u_char *) ((long)adr + 0x100);
 	for (i = 0; i < size; i++)
 		data[i] = readb(ad);
 }
 
-static void
+static inline 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);
+	register u_char *ad = (u_char *) ((long)adr + 0x100);
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
+	}
 }
 
 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));
+	register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
 	for (i = 0; i < size; i++)
 		data[i] = readb(ad);
 }
@@ -109,749 +115,206 @@
 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--;
+	register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
 	}
-	if (!to)
-		printk(KERN_WARNING "Teles0: waitforCEC timeout\n");
 }
 
+/* Interface functions */
 
-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)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	long flags;
-
-	save_flags(flags);
-	cli();
-	waitforCEC(adr, hscx);
-	writehscx(adr, hscx, HSCX_CMDR, data);
-	restore_flags(flags);
+	return (readisac(cs->hw.teles0.membase, offset));
 }
 
-/*
- * fast interrupt here
- */
-
-
 static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	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));
+	writeisac(cs->hw.teles0.membase, offset, value);
 }
 
-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)
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
 }
 
 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)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
 }
 
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	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);
-	}
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
 }
 
 static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	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);
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
 }
 
-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);
-		}
-	}
-}
+/*
+ * fast interrupt HSCX stuff goes here
+ */
 
-static inline void
-hscx_int_main(struct IsdnCardState *sp, u_char val)
-{
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
 
-	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);
-	}
-}
+#include "hscx_irq.c"
 
 static void
-telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, stat = 0;
+	int count = 0;
 
-	sp = (struct IsdnCardState *) irq2dev_map[intno];
-
-	if (!sp) {
+	if (!cs) {
 		printk(KERN_WARNING "Teles0: Spurious interrupt!\n");
 		return;
 	}
-	val = readhscx(sp->membase, 1, HSCX_ISTA);
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 		stat |= 1;
 	}
-	val = readisac(sp->membase, ISAC_ISTA);
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, val);
 		stat |= 2;
 	}
-	val = readhscx(sp->membase, 1, HSCX_ISTA);
-	if (val) {
-		if (sp->debug & L1_DEB_HSCX)
-			debugl1(sp, "HSCX IntStat after IntRoutine");
+	count++;
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (val && count < 20) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "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");
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if (val && count < 20) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "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);
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
 	}
 	if (stat & 2) {
-		writeisac(sp->membase, ISAC_MASK, 0xFF);
-		writeisac(sp->membase, ISAC_MASK, 0x0);
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
 	}
 }
 
-
-static void
-initisac(struct IsdnCardState *sp)
+void
+release_io_teles0(struct IsdnCardState *cs)
 {
-	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);
+	if (cs->hw.teles0.cfg_reg)
+		release_region(cs->hw.teles0.cfg_reg, 8);
 }
 
-static void
-modehscx(struct HscxState *hs, int mode, int ichan)
+static int
+reset_teles0(struct IsdnCardState *cs)
 {
-	struct IsdnCardState *sp = hs->sp;
-	int hscx = hs->hscx;
+	u_char cfval;
+	long flags;
 
-	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;
+	save_flags(flags);
+	sti();
+	if (cs->hw.teles0.cfg_reg) {
+		switch (cs->irq) {
+			case 2:
+			case 9:
+				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:
+				return(1);
+		}
+		cfval |= ((cs->hw.teles0.membase >> 9) & 0xF0);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval);
+		HZDELAY(HZ / 10 + 1);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
 	}
-	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);
+	writeb(0, cs->hw.teles0.membase + 0x80); mb();
+	HZDELAY(HZ / 5 + 1);
+	writeb(1, cs->hw.teles0.membase + 0x80); mb();
+	HZDELAY(HZ / 5 + 1);
+	restore_flags(flags);
+	return(0);
 }
 
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
 {
-	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);
-		}
+	switch (mt) {
+		case CARD_RESET:
+			reset_teles0(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles0(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &teles0_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_teles0(struct IsdnCard *card)
+__initfunc(int
+setup_teles0(struct IsdnCard *card))
 {
-	u_char cfval, val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
-	long flags;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	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))
+	printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0))
 		return (0);
 
-	if (sp->typ == ISDN_CTYPE_16_0)
-		sp->cfg_reg = card->para[2];
+	if (cs->typ == ISDN_CTYPE_16_0)
+		cs->hw.teles0.cfg_reg = card->para[2];
 	else			/* 8.0 */
-		sp->cfg_reg = 0;
+		cs->hw.teles0.cfg_reg = 0;
 
 	if (card->para[1] < 0x10000) {
 		card->para[1] <<= 4;
@@ -859,110 +322,69 @@
 		   "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)) {
+	cs->hw.teles0.membase = card->para[1];
+	cs->irq = card->para[0];
+	if (cs->hw.teles0.cfg_reg) {
+		if (check_region((cs->hw.teles0.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);
+			       cs->hw.teles0.cfg_reg,
+			       cs->hw.teles0.cfg_reg + 8);
 			return (0);
 		} else {
-			request_region(sp->cfg_reg, 8, "teles cfg");
+			request_region(cs->hw.teles0.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) {
+	if (cs->hw.teles0.cfg_reg) {
+		if ((val = bytein(cs->hw.teles0.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);
+			       cs->hw.teles0.cfg_reg + 0, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
 			return (0);
 		}
-		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+		if ((val = bytein(cs->hw.teles0.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);
+			       cs->hw.teles0.cfg_reg + 1, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
 			return (0);
 		}
-		val = bytein(sp->cfg_reg + 2);	/* 0x1e=without AB
-						   * 0x1f=with AB
-						   * 0x1c 16.3 ???
-						 */
+		val = bytein(cs->hw.teles0.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);
+			       cs->hw.teles0.cfg_reg + 2, val);
+			release_region(cs->hw.teles0.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)) {
+	/* 16.0 and 8.0 designed for IOM1 */
+	test_and_set_bit(HW_IOM1, &cs->HW_Flags);
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:0x%X cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase, cs->hw.teles0.cfg_reg);
+	if (reset_teles0(cs)) {
+		printk(KERN_WARNING "Teles0: wrong IRQ\n");
+		release_io_teles0(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	ISACVersion(cs, "Teles0:");
+	if (HscxVersion(cs, "Teles0:")) {
 		printk(KERN_WARNING
 		 "Teles0: wrong HSCX versions check IO/MEM addresses\n");
-		release_io_teles0(card);
+		release_io_teles0(cs);
 		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.35/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c
--- v2.0.35/linux/drivers/isdn/hisax/teles3.c	Tue Aug  5 09:00:12 1997
+++ linux/drivers/isdn/hisax/teles3.c	Sun Nov 15 10:32:58 1998
@@ -1,4 +1,4 @@
-/* $Id: teles3.c,v 1.11 1997/04/13 19:54:05 keil Exp $
+/* $Id: teles3.c,v 1.11.2.9 1998/04/08 21:58:52 keil Exp $
 
  * teles3.c     low level stuff for Teles 16.3 & PNP isdn cards
  *
@@ -11,6 +11,30 @@
  *              Beat Doebeli
  *
  * $Log: teles3.c,v $
+ * Revision 1.11.2.9  1998/04/08 21:58:52  keil
+ * New init code
+ *
+ * Revision 1.11.2.8  1998/01/27 22:37:46  keil
+ * fast io
+ *
+ * Revision 1.11.2.7  1998/01/11 22:58:01  keil
+ * make IRQ 9 working again
+ *
+ * Revision 1.11.2.6  1997/12/01 22:35:43  keil
+ * ID Byte for 16.3 version 1.1
+ *
+ * Revision 1.11.2.5  1997/11/15 18:51:03  keil
+ * new common init function
+ *
+ * Revision 1.11.2.4  1997/10/17 22:14:30  keil
+ * update to last hisax version
+ *
+ * Revision 2.1  1997/07/27 21:47:12  keil
+ * new interface structures
+ *
+ * Revision 2.0  1997/06/26 11:02:46  keil
+ * New Layer and card interface
+ *
  * Revision 1.11  1997/04/13 19:54:05  keil
  * Change in IRQ check delay for SMP
  *
@@ -35,36 +59,20 @@
  * 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
- *
- *
+ * removed old log info /KKe
  *
  */
 #define __NO_VERSION__
-#include "siemens.h"
 #include "hisax.h"
-#include "teles3.h"
+#include "isac.h"
+#include "hscx.h"
 #include "isdnl1.h"
-#include <linux/kernel_stat.h>
 
 extern const char *CardType[];
-const char *teles3_revision = "$Revision: 1.11 $";
+const char *teles3_revision = "$Revision: 1.11.2.9 $";
 
-#define byteout(addr,val) outb_p(val,addr)
-#define bytein(addr) inb_p(addr)
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
 
 static inline u_char
 readreg(unsigned int adr, u_char off)
@@ -82,953 +90,407 @@
 static inline void
 read_fifo(unsigned int adr, u_char * data, int size)
 {
-	insb(adr + 0x1e, data, size);
+	insb(adr, 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");
+	outsb(adr, data, size);
 }
-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);
-}
+/* Interface functions */
 
-/*
- * fast interrupt here
- */
-
-static void
-hscxreport(struct IsdnCardState *sp, int hscx)
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
 {
-	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));
+	return (readreg(cs->hw.teles3.isac, offset));
 }
 
-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)
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
 {
-	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);
-	}
+	writereg(cs->hw.teles3.isac, offset, value);
 }
 
 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)
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	read_fifo(cs->hw.teles3.isacfifo, data, size);
 }
 
-/*
- * ISAC stuff goes here
- */
-
 static void
-isac_empty_fifo(struct IsdnCardState *sp, int count)
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
 {
-	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);
-	}
+	write_fifo(cs->hw.teles3.isacfifo, data, size);
 }
 
-static void
-isac_fill_fifo(struct IsdnCardState *sp)
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
 {
-	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);
-	}
+	return (readreg(cs->hw.teles3.hscx[hscx], offset));
 }
 
 static void
-ph_command(struct IsdnCardState *sp, unsigned int command)
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
 {
-	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);
+	writereg(cs->hw.teles3.hscx[hscx], offset, value);
 }
 
+/*
+ * fast interrupt HSCX stuff goes here
+ */
 
-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)
-{
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
 
-	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);
-	}
-}
+#include "hscx_irq.c"
 
 static void
 teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs)
 {
 #define MAXCOUNT 20
-	struct IsdnCardState *sp;
+	struct IsdnCardState *cs = dev_id;
 	u_char val, stat = 0;
 	int count = 0;
 
-	sp = (struct IsdnCardState *) irq2dev_map[intno];
-
-	if (!sp) {
+	if (!cs) {
 		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
 		return;
 	}
-	val = readreg(sp->hscx[1], HSCX_ISTA);
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
       Start_HSCX:
 	if (val) {
-		hscx_int_main(sp, val);
+		hscx_int_main(cs, val);
 		stat |= 1;
 	}
-	val = readreg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
       Start_ISAC:
 	if (val) {
-		isac_interrupt(sp, val);
+		isac_interrupt(cs, val);
 		stat |= 2;
 	}
 	count++;
-	val = readreg(sp->hscx[1], HSCX_ISTA);
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
 	if (val && count < MAXCOUNT) {
-		if (sp->debug & L1_DEB_HSCX)
-			debugl1(sp, "HSCX IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
 		goto Start_HSCX;
 	}
-	val = readreg(sp->isac, ISAC_ISTA);
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
 	if (val && count < MAXCOUNT) {
-		if (sp->debug & L1_DEB_ISAC)
-			debugl1(sp, "ISAC IntStat after IntRoutine");
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "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);
+		writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+		writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+		writereg(cs->hw.teles3.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(cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+		writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0);
 	}
-	writereg(sp->hscx[hscx], HSCX_ISTA, 0x00);
 }
 
 inline static void
-release_ioregs(struct IsdnCard *card, int mask)
+release_ioregs(struct IsdnCardState *cs, int mask)
 {
 	if (mask & 1)
-		release_region(card->sp->isac, 32);
+		release_region(cs->hw.teles3.isac + 32, 32);
 	if (mask & 2)
-		release_region(card->sp->hscx[0], 32);
+		release_region(cs->hw.teles3.hscx[0] + 32, 32);
 	if (mask & 4)
-		release_region(card->sp->hscx[1], 32);
+		release_region(cs->hw.teles3.hscx[1] + 32, 32);
 }
 
 void
-release_io_teles3(struct IsdnCard *card)
+release_io_teles3(struct IsdnCardState *cs)
 {
-	if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA)
-		release_region(card->sp->hscx[0], 97);
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA)
+		release_region(cs->hw.teles3.cfg_reg, 97);
 	else {
-		if (card->sp->cfg_reg)
-			release_region(card->sp->cfg_reg, 8);
-		release_ioregs(card, 0x7);
+		if (cs->hw.teles3.cfg_reg)
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				release_region(cs->hw.teles3.cfg_reg, 1);
+			} else {
+				release_region(cs->hw.teles3.cfg_reg, 8);
+			}
+		release_ioregs(cs, 0x7);
 	}
 }
 
-static void
-clear_pending_ints(struct IsdnCardState *sp)
+static int
+reset_teles3(struct IsdnCardState *cs)
 {
-	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);
-}
+	long flags;
+	u_char irqcfg;
 
-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);
-		}
+	if (cs->typ != ISDN_CTYPE_TELESPCMCIA) {
+		if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+			switch (cs->irq) {
+				case 2:
+				case 9:
+					irqcfg = 0x00;
+					break;
+				case 3:
+					irqcfg = 0x02;
+					break;
+				case 4:
+					irqcfg = 0x04;
+					break;
+				case 5:
+					irqcfg = 0x06;
+					break;
+				case 10:
+					irqcfg = 0x08;
+					break;
+				case 11:
+					irqcfg = 0x0A;
+					break;
+				case 12:
+					irqcfg = 0x0C;
+					break;
+				case 15:
+					irqcfg = 0x0E;
+					break;
+				default:
+					return(1);
+			}
+			save_flags(flags);
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg);
+			sti();
+			HZDELAY(HZ / 10 + 1);
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1);
+			HZDELAY(HZ / 10 + 1);
+			restore_flags(flags);
+		} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+			save_flags(flags);
+			byteout(cs->hw.teles3.cfg_reg, 0xff);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.cfg_reg, 0x00);
+			HZDELAY(2);
+			restore_flags(flags);
+		} else {
+			/* Reset off for 16.3 PnP , thanks to Georg Acher */
+			save_flags(flags);
+			byteout(cs->hw.teles3.isac + 0x3c, 0);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.isac + 0x3c, 1);
+			HZDELAY(2);
+			restore_flags(flags);
+		}
+	}
+	return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			reset_teles3(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles3(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &teles3_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
 	}
-	return (ret);
+	return(0);
 }
 
-int
-setup_teles3(struct IsdnCard *card)
+__initfunc(int
+setup_teles3(struct IsdnCard *card))
 {
-	u_char cfval = 0, val, verA, verB;
-	struct IsdnCardState *sp = card->sp;
-	long flags;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
 	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))
+	printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP)
+	    && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA))
 		return (0);
 
-	if (sp->typ == ISDN_CTYPE_16_3) {
-		sp->cfg_reg = card->para[1];
-		switch (sp->cfg_reg) {
+	if (cs->typ == ISDN_CTYPE_16_3) {
+		cs->hw.teles3.cfg_reg = card->para[1];
+		switch (cs->hw.teles3.cfg_reg) {
 			case 0x180:
 			case 0x280:
 			case 0x380:
-				sp->cfg_reg |= 0xc00;
+				cs->hw.teles3.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)) {
+		cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420;
+		cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20;
+		cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820;
+	} else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		cs->hw.teles3.cfg_reg = card->para[1];
+		cs->hw.teles3.hscx[0] = card->para[1] - 0x20;
+		cs->hw.teles3.hscx[1] = card->para[1];
+		cs->hw.teles3.isac = card->para[1] + 0x20;
+	} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+		cs->hw.teles3.cfg_reg = card->para[3];
+		cs->hw.teles3.isac = card->para[2] - 32;
+		cs->hw.teles3.hscx[0] = card->para[1] - 32;
+		cs->hw.teles3.hscx[1] = card->para[1];
+	} else {	/* PNP */
+		cs->hw.teles3.cfg_reg = 0;
+		cs->hw.teles3.isac = card->para[1] - 32;
+		cs->hw.teles3.hscx[0] = card->para[2] - 32;
+		cs->hw.teles3.hscx[1] = card->para[2];
+	}
+	cs->irq = card->para[0];
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		if (check_region((cs->hw.teles3.cfg_reg), 97)) {
 			printk(KERN_WARNING
 			       "HiSax: %s ports %x-%x already in use\n",
-			       CardType[sp->typ],
-			       sp->hscx[0],
-			       sp->hscx[0] + 96);
+			       CardType[cs->typ],
+			       cs->hw.teles3.cfg_reg,
+			       cs->hw.teles3.cfg_reg + 96);
 			return (0);
 		} else
-			request_region(sp->hscx[0], 97, "HiSax Teles PCMCIA");
+			request_region(cs->hw.teles3.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);
+		if (cs->hw.teles3.cfg_reg) {
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				if (check_region((cs->hw.teles3.cfg_reg), 1)) {
+					printk(KERN_WARNING
+						"HiSax: %s config port %x already in use\n",
+						CardType[card->typ],
+						cs->hw.teles3.cfg_reg);
+					return (0);
+				} else
+					request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg");
 			} else {
-				request_region(sp->cfg_reg, 8, "teles3 cfg");
+				if (check_region((cs->hw.teles3.cfg_reg), 8)) {
+					printk(KERN_WARNING
+					       "HiSax: %s config port %x-%x already in use\n",
+					       CardType[card->typ],
+					       cs->hw.teles3.cfg_reg,
+						cs->hw.teles3.cfg_reg + 8);
+					return (0);
+				} else
+					request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg");
 			}
 		}
-		if (check_region((sp->isac), 32)) {
+		if (check_region((cs->hw.teles3.isac + 32), 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);
-			}
+			       CardType[cs->typ],
+			       cs->hw.teles3.isac + 32,
+			       cs->hw.teles3.isac + 64);
+			if (cs->hw.teles3.cfg_reg)
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
 			return (0);
-		} else {
-			request_region(sp->isac, 32, "HiSax isac");
-		}
-		if (check_region((sp->hscx[0]), 32)) {
+		} else
+			request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac");
+		if (check_region((cs->hw.teles3.hscx[0] + 32), 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);
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[0] + 32,
+			       cs->hw.teles3.hscx[0] + 64);
+			if (cs->hw.teles3.cfg_reg)
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			release_ioregs(cs, 1);
 			return (0);
-		} else {
-			request_region(sp->hscx[0], 32, "HiSax hscx A");
-		}
-		if (check_region((sp->hscx[1]), 32)) {
+		} else
+			request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A");
+		if (check_region((cs->hw.teles3.hscx[1] + 32), 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);
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[1] + 32,
+			       cs->hw.teles3.hscx[1] + 64);
+			if (cs->hw.teles3.cfg_reg)
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			release_ioregs(cs, 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;
-		}
+		} else
+			request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B");
 	}
-	if (sp->cfg_reg) {
-		if ((val = bytein(sp->cfg_reg + 0)) != 0x51) {
+	if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+		if ((val = bytein(cs->hw.teles3.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);
+			       cs->hw.teles3.cfg_reg + 0, val);
+			release_io_teles3(cs);
 			return (0);
 		}
-		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+		if ((val = bytein(cs->hw.teles3.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);
+			       cs->hw.teles3.cfg_reg + 1, val);
+			release_io_teles3(cs);
 			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) {
+		val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB
+							 * 0x1f=with AB
+							 * 0x1c 16.3 ???
+							 * 0x39 16.3 1.1
+							 * 0x46 16.3 with AB + Video (Teles-Vision)
+							 */
+		if (val != 0x46 && val != 0x39 && 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);
+			       cs->hw.teles3.cfg_reg + 2, val);
+			release_io_teles3(cs);
 			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_INFO
+	       "HiSax: %s config irq:%d isac:0x%X  cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X  hscx B:0x%X\n",
+	       cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32);
+
+	if (reset_teles3(cs)) {
+		printk(KERN_WARNING "Teles3: wrong IRQ\n");
+		release_io_teles3(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	ISACVersion(cs, "Teles3:");
+	if (HscxVersion(cs, "Teles3:")) {
 		printk(KERN_WARNING
 		       "Teles3: wrong HSCX versions check IO address\n");
-		release_io_teles3(card);
+		release_io_teles3(cs);
 		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.35/linux/drivers/isdn/hisax/teles3c.c linux/drivers/isdn/hisax/teles3c.c
--- v2.0.35/linux/drivers/isdn/hisax/teles3c.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles3c.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,196 @@
+/* $Id: teles3c.c,v 1.1.2.3 1998/11/03 00:07:40 keil Exp $
+
+ * teles3c.c     low level stuff for teles 16.3c
+ *
+ * Author     Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: teles3c.c,v $
+ * Revision 1.1.2.3  1998/11/03 00:07:40  keil
+ * certification related changes
+ * fixed logging for smaller stack use
+ *
+ * Revision 1.1.2.2  1998/01/27 22:40:37  keil
+ * fixed IRQ latency, B-channel selection and more
+ *
+ * Revision 1.1.2.1  1998/01/11 22:54:04  keil
+ * Teles 16.3c (HFC 2BDS0) first version
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *teles163c_revision = "$Revision: 1.1.2.3 $";
+
+static void
+t163c_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat;
+
+	if (!cs) {
+		printk(KERN_WARNING "teles3c: Spurious interrupt!\n");
+		return;
+	}
+	if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & 
+		(stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) {
+		val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "teles3c: stat(%02x) s1(%02x)", stat, val);
+		hfc2bds0_interrupt(cs, val);
+	} else {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "teles3c: irq_no_irq stat(%02x)", stat);
+	}
+}
+
+static void
+t163c_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcD.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80);
+	add_timer(&cs->hw.hfcD.timer);
+*/
+}
+
+void
+release_io_t163c(struct IsdnCardState *cs)
+{
+	release2bds0(cs);
+	del_timer(&cs->hw.hfcD.timer);
+	if (cs->hw.hfcD.addr)
+		release_region(cs->hw.hfcD.addr, 2);
+}
+
+static void
+reset_t163c(struct IsdnCardState *cs)
+{
+	long flags;
+
+	printk(KERN_INFO "teles3c: resetting card\n");
+	cs->hw.hfcD.cirm = HFCD_RESET | HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset On */
+	save_flags(flags);
+	sti();
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 3;
+	schedule();
+	cs->hw.hfcD.cirm = HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset Off */
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + 1;
+	schedule();
+	cs->hw.hfcD.cirm |= HFCD_INTB;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* INT B */
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */
+	cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE;
+	cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS |
+		HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC |
+		HFCD_INTS_DREC | HFCD_INTS_L1STATE;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */
+	udelay(10);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */
+	cs->hw.hfcD.mst_m = 0;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, HFCD_MASTER); /* HFC Master */
+	cs->hw.hfcD.sctrl = 0;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+	restore_flags(flags);
+}
+
+static int
+t163c_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "teles3c: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			reset_t163c(cs);
+			return(0);
+		case CARD_RELEASE:
+			release_io_t163c(cs);
+			return(0);
+		case CARD_SETIRQ:
+			cs->hw.hfcD.timer.expires = jiffies + 75;
+			add_timer(&cs->hw.hfcD.timer);
+			return(request_irq(cs->irq, &t163c_interrupt,
+					I4L_IRQ_FLAG, "HiSax", cs));
+		case CARD_INIT:
+			init2bds0(cs);
+			save_flags(flags);
+			sti();
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + (80*HZ)/1000;
+			schedule();
+			cs->hw.hfcD.ctmt |= HFCD_TIM800;
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); 
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			restore_flags(flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+__initfunc(int
+setup_t163c(struct IsdnCard *card))
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, teles163c_revision);
+	printk(KERN_INFO "HiSax: Teles 16.3c driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELES3C)
+		return (0);
+	cs->debug = 0xff;
+	cs->hw.hfcD.addr = card->para[1] & 0xfffe;
+	cs->irq = card->para[0];
+	cs->hw.hfcD.cip = 0;
+	cs->hw.hfcD.int_s1 = 0;
+	cs->hw.hfcD.send = NULL;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfcD.bfifosize = 1024 + 512;
+	cs->hw.hfcD.dfifosize = 512;
+	cs->ph_state = 0;
+	cs->hw.hfcD.fifo = 255;
+	if (check_region((cs->hw.hfcD.addr), 2)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfcD.addr,
+		       cs->hw.hfcD.addr + 2);
+		return (0);
+	} else {
+		request_region(cs->hw.hfcD.addr, 2, "teles3c isdn");
+	}
+	/* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */
+	outb(0x00, cs->hw.hfcD.addr);
+	outb(0x56, cs->hw.hfcD.addr | 1);
+	printk(KERN_INFO
+	       "teles3c: defined at 0x%x IRQ %d HZ %d\n",
+	       cs->hw.hfcD.addr,
+	       cs->irq, HZ);
+
+	set_cs_func(cs);
+	cs->hw.hfcD.timer.function = (void *) t163c_Timer;
+	cs->hw.hfcD.timer.data = (long) cs;
+	init_timer(&cs->hw.hfcD.timer);
+	reset_t163c(cs);
+	cs->cardmsg = &t163c_card_msg;
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/hisax/telespci.c linux/drivers/isdn/hisax/telespci.c
--- v2.0.35/linux/drivers/isdn/hisax/telespci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/telespci.c	Sun Nov 15 10:32:58 1998
@@ -0,0 +1,377 @@
+/* $Id: telespci.c,v 1.1.2.3 1998/10/16 12:46:09 keil Exp $
+
+ * telespci.c     low level stuff for Teles PCI isdn cards
+ *
+ * Author       Ton van Rosmalen 
+ *              Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: telespci.c,v $
+ * Revision 1.1.2.3  1998/10/16 12:46:09  keil
+ * fix pci detection for more as one card
+ *
+ * Revision 1.1.2.2  1998/04/20 08:52:46  keil
+ * Fix register offsets
+ *
+ * Revision 1.1.2.1  1998/04/11 18:44:42  keil
+ * New files
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+extern const char *CardType[];
+
+const char *telespci_revision = "$Revision: 1.1.2.3 $";
+
+#define ZORAN_PO_RQ_PEN	0x02000000
+#define ZORAN_PO_WR	0x00800000
+#define ZORAN_PO_GID0	0x00000000
+#define ZORAN_PO_GID1	0x00100000
+#define ZORAN_PO_GREG0	0x00000000
+#define ZORAN_PO_GREG1	0x00010000
+#define ZORAN_PO_DMASK	0xFF
+
+#define WRITE_ADDR_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
+#define READ_DATA_ISAC	(ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_DATA_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_ADDR_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0)
+#define READ_DATA_HSCX	(ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+#define WRITE_DATA_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+
+#define ZORAN_WAIT_NOBUSY	do { \
+					portdata = readl(adr + 0x200); \
+				} while (portdata & ZORAN_PO_RQ_PEN)
+
+static inline u_char
+readisac(unsigned int adr, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from ISAC */
+	writel(READ_DATA_ISAC, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writeisac(unsigned int adr, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to ISAC */
+	writel(WRITE_DATA_ISAC | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline u_char
+readhscx(unsigned int adr, int hscx, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from HSCX */
+	writel(READ_DATA_HSCX, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return ((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to HSCX */
+	writel(WRITE_DATA_HSCX | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_ISAC, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char)(portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static void
+write_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_ISAC | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+	}
+}
+
+static inline void
+read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_HSCX, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char) (portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+	unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_HSCX | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		udelay(10);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat = 0;
+
+	if (!cs) {
+		printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n");
+		return;
+	}
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (val) {
+		hscx_int_main(cs, val);
+		stat |= 1;
+	}
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if (val) {
+		isac_interrupt(cs, val);
+		stat |= 2;
+	}
+	/* Clear interrupt register for Zoran PCI controller */
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+
+	if (stat & 1) {
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+		writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+		writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+		writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+	}
+}
+
+void
+release_io_telespci(struct IsdnCardState *cs)
+{
+	vfree((void *)cs->hw.teles0.membase);
+}
+
+static int
+TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_telespci(cs);
+			return(0);
+		case CARD_SETIRQ:
+			return(request_irq(cs->irq, &telespci_interrupt,
+					I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs));
+		case CARD_INIT:
+			inithscxisac(cs, 3);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static 	int pci_index __initdata = 0;
+
+__initfunc(int
+setup_telespci(struct IsdnCard *card))
+{
+	int found=0;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_char pci_bus, pci_device_fn, pci_irq;
+	u_int pci_memaddr;
+
+	strcpy(tmp, telespci_revision);
+	printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELESPCI)
+		return (0);
+
+#if CONFIG_PCI
+	for (; pci_index < 0xff; pci_index++) {
+		if (pcibios_find_device (0x11DE, 0x6120,
+			pci_index, &pci_bus, &pci_device_fn)
+			== PCIBIOS_SUCCESSFUL) {
+			found = 1;
+		} else {
+			break;
+		}
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+				PCI_BASE_ADDRESS_0, &pci_memaddr);
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+				PCI_INTERRUPT_LINE, &pci_irq);
+
+		printk(KERN_INFO "Found: Zoran, base-address: 0x%x,"
+			" irq: 0x%x\n", pci_memaddr, pci_irq);
+		break;
+	}
+	if (!found) {
+		printk(KERN_WARNING "TelesPCI: No PCI card found\n");
+		return(0);
+	}
+	pci_index++;
+	cs->hw.teles0.membase = (u_int) vremap(pci_memaddr, PAGE_SIZE);
+	cs->irq = pci_irq;
+#else
+	printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n");
+	printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n");
+	return (0);
+#endif /* CONFIG_PCI */
+
+	/* Initialize Zoran PCI controller */
+	writel(0x00000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C);
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+	writel(0x61000000, cs->hw.teles0.membase + 0x40);
+	/* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:%x\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &TelesPCI_card_msg;
+	ISACVersion(cs, "TelesPCI:");
+	if (HscxVersion(cs, "TelesPCI:")) {
+		printk(KERN_WARNING
+		 "TelesPCI: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_telespci(cs);
+		return (0);
+	}
+	return (1);
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- v2.0.35/linux/drivers/isdn/icn/icn.c	Tue Aug  5 17:10:02 1997
+++ linux/drivers/isdn/icn/icn.c	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: icn.c,v 1.45 1997/06/21 10:42:06 fritz Exp $
+ /* $Id: icn.c,v 1.45.2.4 1998/11/05 22:13:02 fritz Exp $
 
  * ISDN low-level module for the ICN active ISDN-Card.
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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
@@ -19,6 +19,28 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.c,v $
+ * Revision 1.45.2.4  1998/11/05 22:13:02  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.45.2.3  1998/06/07 13:32:04  fritz
+ * Minor bugfixes for broken Switches.
+ *
+ * Revision 1.45.2.2  1998/03/07 23:35:36  detabc
+ * added the abc-extension to the linux isdn-kernel
+ * for kernel-version 2.0.xx
+ * DO NOT USE FOR HIGHER KERNELS-VERSIONS
+ * all source-lines are switched with the define  CONFIG_ISDN_WITH_ABC
+ * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes
+ *
+ * you need also a modified isdnctrl-source the switch on the
+ * features of the abc-extension
+ *
+ * please use carefully. more detail will be follow.
+ * thanks
+ *
+ * Revision 1.45.2.1  1997/08/21 15:56:50  fritz
+ * Synchronized 2.0.X branch with 2.0.31-pre7
+ *
  * Revision 1.45  1997/06/21 10:42:06  fritz
  * Added availability to select leased mode on only one channel.
  *
@@ -193,7 +215,7 @@
 #undef MAP_DEBUG
 
 static char
-*revision = "$Revision: 1.45 $";
+*revision = "$Revision: 1.45.2.4 $";
 
 static int icn_addcard(int, char *, char *);
 
@@ -531,8 +553,13 @@
 {
 	{"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     */
+#ifdef CONFIG_ISDN_WITH_ABC
+	{"DCON_",          ISDN_STAT_DCONN, 10}, /* D-Channel connected       */
+	{"DDIS_",          ISDN_STAT_DHUP,  11}, /* D-Channel disconnected    */
+#else
+	{"DCON_",          ISDN_STAT_DCONN, 0}, /* D-Channel connected        */
+	{"DDIS_",          ISDN_STAT_DHUP,  0}, /* D-Channel disconnected     */
+#endif
 	{"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  */
@@ -582,7 +609,41 @@
 	cmd.driver = card->myid;
 	cmd.arg = channel;
 	switch (action) {
+#ifdef CONFIG_ISDN_WITH_ABC
+	case 11:
+
+		save_flags(flags);
+		cli();
+		icn_free_queue(card,channel);
+		card->rcvidx[channel] = 0;
+
+		if( card->flags & 
+			((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) {
+
+			isdn_ctrl ncmd;
+
+			printk(KERN_INFO "icn: D-Channel hangup before B-Channel hangup\n");
+
+			card->flags &= ~((channel)?
+					ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
+
+			memset(&ncmd,0,sizeof(ncmd));
+
+			ncmd.driver = card->myid;
+			ncmd.arg = channel;
+			ncmd.command = ISDN_STAT_BHUP;
+			restore_flags(flags);
+			card->interface.statcallb(&cmd);
+			dflag |= (channel+1);
+
+		} else restore_flags(flags);
+		
+		break;
+#endif
 		case 1:
+#ifdef CONFIG_ISDN_WITH_ABC
+			icn_free_queue(card,channel);
+#endif
 			card->flags |= (channel) ?
 			    ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;
 			break;
@@ -601,17 +662,22 @@
 				char *t = status + 6;
 				char *s = strpbrk(t, ",");
 
+				memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup));
+				if (!s)
+					break;
 				*s++ = '\0';
 				strncpy(cmd.parm.setup.phone, t,
 					sizeof(cmd.parm.setup.phone));
-				s = strpbrk(t = s, ",");
+				if (!(s = strpbrk(t = s, ",")))
+					break;
 				*s++ = '\0';
 				if (!strlen(t))
 					cmd.parm.setup.si1 = 0;
 				else
 					cmd.parm.setup.si1 =
 					    simple_strtoul(t, NULL, 10);
-				s = strpbrk(t = s, ",");
+				if (!(s = strpbrk(t = s, ",")))
+					break;
 				*s++ = '\0';
 				if (!strlen(t))
 					cmd.parm.setup.si2 = 0;
@@ -621,8 +687,6 @@
 				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);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h
--- v2.0.35/linux/drivers/isdn/icn/icn.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/icn/icn.h	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $
+/* $Id: icn.h,v 1.26.2.1 1998/11/05 22:13:06 fritz Exp $
 
  * ISDN lowlevel-module for the ICN active ISDN-Card.
  *
- * Copyright 1994 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.h,v $
+ * Revision 1.26.2.1  1998/11/05 22:13:06  fritz
+ * Changed mail-address.
+ *
  * Revision 1.26  1997/02/14 12:23:16  fritz
  * Added support for new insmod parameter handling.
  *
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- v2.0.35/linux/drivers/isdn/isdn_audio.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_audio.c	Sun Nov 15 10:32:59 1998
@@ -1,9 +1,10 @@
-/* $Id: isdn_audio.c,v 1.8 1997/03/02 14:29:16 fritz Exp $
+/* $Id: isdn_audio.c,v 1.8.2.2 1998/11/05 22:11:35 fritz Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de)
  * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
+ * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.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
@@ -20,6 +21,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.c,v $
+ * Revision 1.8.2.2  1998/11/05 22:11:35  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.8.2.1  1998/08/22 16:43:04  armin
+ * Added silence detection in audio receive mode (AT+VSD).
+ *
  * Revision 1.8  1997/03/02 14:29:16  fritz
  * More ttyI related cleanup.
  *
@@ -53,7 +60,7 @@
 #include "isdn_audio.h"
 #include "isdn_common.h"
 
-char *isdn_audio_revision = "$Revision: 1.8 $";
+char *isdn_audio_revision = "$Revision: 1.8.2.2 $";
 
 /*
  * Misc. lookup-tables.
@@ -652,4 +659,93 @@
 		}
 		len -= c;
 	}
+}
+
+silence_state *
+isdn_audio_silence_init(silence_state * s)
+{
+        if (!s)
+                s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC);
+        if (s) {
+                s->idx = 0;
+                s->state = 0;
+        }
+        return s;
+}
+
+void
+isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+        silence_state *s = info->silence_state;
+        int i;
+        signed char c;
+
+        if ((!s) || (!info->emu.vpar[1])) return;
+
+        for (i = 0; i < len; i++) {
+                if (fmt)
+                    c = isdn_audio_alaw_to_ulaw[*buf++];
+                        else
+                    c = *buf++;
+
+                if (c > 0) c -= 128;
+                c = abs(c);
+
+                if (c > (info->emu.vpar[1] * 4)) {
+                        s->idx = 0;
+                        s->state = 1;
+                } else {
+                        if (s->idx < 210000) s->idx++;
+                }
+        }
+}
+
+void
+isdn_audio_eval_silence(modem_info * info)
+{
+        silence_state *s = info->silence_state;
+        struct sk_buff *skb;
+        unsigned long flags;
+        int di;
+        int ch;
+        char what;
+        char *p;
+
+	if (!s) return;
+        what = ' ';
+        if (s->idx > (info->emu.vpar[2] * 800)) {
+                s->idx = 0;
+                if (!s->state) {        /* silence from beginning of rec */
+                        what = 's';
+                } else {
+                        what = 'q';
+                }
+        }
+                if ((what == 's') || (what == 'q')) {
+                        printk(KERN_DEBUG "ttyI%d: %s\n", info->line,
+                                (what=='s') ? "silence":"quiet");
+                        skb = dev_alloc_skb(2);
+                        p = (char *) skb_put(skb, 2);
+                        p[0] = 0x10;
+                        p[1] = what;
+                        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]);
+                }
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_audio.h linux/drivers/isdn/isdn_audio.h
--- v2.0.35/linux/drivers/isdn/isdn_audio.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_audio.h	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_audio.h,v 1.5 1997/02/03 22:45:21 fritz Exp $
+/* $Id: isdn_audio.h,v 1.5.2.2 1998/11/05 22:11:39 fritz Exp $
 
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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
@@ -19,6 +19,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.h,v $
+ * Revision 1.5.2.2  1998/11/05 22:11:39  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.5.2.1  1998/08/22 16:43:06  armin
+ * Added silence detection in audio receive mode (AT+VSD).
+ *
  * Revision 1.5  1997/02/03 22:45:21  fritz
  * Reformatted according CodingStyle
  *
@@ -51,6 +57,11 @@
 	int buf[DTMF_NPOINTS];
 } dtmf_state;
 
+typedef struct silence_state {
+        int state;
+        unsigned int idx;
+} silence_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);
@@ -60,3 +71,6 @@
 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 *);
+extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_silence(modem_info *);
+silence_state *isdn_audio_silence_init(silence_state *);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c
--- v2.0.35/linux/drivers/isdn/isdn_cards.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_cards.c	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_cards.c,v 1.6 1997/04/23 18:56:03 fritz Exp $
+/* $Id: isdn_cards.c,v 1.6.2.1 1998/11/05 22:11:40 fritz Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.c,v $
+ * Revision 1.6.2.1  1998/11/05 22:11:40  fritz
+ * Changed mail-address.
+ *
  * Revision 1.6  1997/04/23 18:56:03  fritz
  * Old Teles driver removed, Changed doc and scripts accordingly.
  *
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_cards.h linux/drivers/isdn/isdn_cards.h
--- v2.0.35/linux/drivers/isdn/isdn_cards.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_cards.h	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_cards.h,v 1.2 1997/02/03 23:31:55 fritz Exp $
+/* $Id: isdn_cards.h,v 1.2.2.1 1998/11/05 22:11:42 fritz Exp $
 
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.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
@@ -19,6 +19,9 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.h,v $
+ * Revision 1.2.2.1  1998/11/05 22:11:42  fritz
+ * Changed mail-address.
+ *
  * Revision 1.2  1997/02/03 23:31:55  fritz
  * Reformatted according CodingStyle
  *
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.0.35/linux/drivers/isdn/isdn_common.c	Tue Aug  5 11:05:16 1997
+++ linux/drivers/isdn/isdn_common.c	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_common.c,v 1.44 1997/05/27 15:17:23 fritz Exp $
+/* $Id: isdn_common.c,v 1.44.2.9 1998/11/06 00:07:25 fritz Exp $
 
  * Linux ISDN subsystem, common used functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -21,6 +21,43 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.44.2.9  1998/11/06 00:07:25  fritz
+ * Bugfix: loading additional driver while /dev/isdnctrl opened resulted in
+ *         wrong module usage count after closing /dev/isdnctrl.
+ *
+ * Revision 1.44.2.8  1998/11/05 22:11:43  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.44.2.7  1998/11/04 17:22:40  fritz
+ * Replaced broken lowlevel-driver locking.
+ *
+ * Revision 1.44.2.6  1998/11/03 14:30:56  fritz
+ * Reduced stack usage in various functions.
+ * Adapted statemachine to work with certified HiSax.
+ * Some fixes in callback handling.
+ *
+ * Revision 1.44.2.5  1998/10/25 15:48:04  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
+ * Revision 1.44.2.4  1998/06/07 13:47:44  fritz
+ * ABC cleanup
+ *
+ * Revision 1.44.2.2  1998/03/16 09:55:44  cal
+ * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches.
+ *
+ * Revision 1.44.2.1  1998/03/07 23:35:03  detabc
+ * added the abc-extension to the linux isdn-kernel
+ * for kernel-version 2.0.xx
+ * DO NOT USE FOR HIGHER KERNELS-VERSIONS
+ * all source-lines are switched with the define  CONFIG_ISDN_WITH_ABC
+ * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes
+ *
+ * you need also a modified isdnctrl-source the switch on the
+ * features of the abc-extension
+ *
+ * please use carefully. more detail will be follow.
+ * thanks
+ *
  * Revision 1.44  1997/05/27 15:17:23  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -218,7 +255,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static char *isdn_revision = "$Revision: 1.44 $";
+static char *isdn_revision = "$Revision: 1.44.2.9 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -238,16 +275,39 @@
 void
 isdn_MOD_INC_USE_COUNT(void)
 {
+	int i;
+
 	MOD_INC_USE_COUNT;
+	for (i = 0; i < dev->drivers; i++) {
+		isdn_ctrl cmd;
+
+		cmd.driver = i;
+		cmd.arg = 0;
+		cmd.command = ISDN_CMD_LOCK;
+		isdn_command(&cmd);
+		dev->drv[i]->locks++;
+	}
 }
 
 void
 isdn_MOD_DEC_USE_COUNT(void)
 {
+	int i;
+
 	MOD_DEC_USE_COUNT;
+	for (i = 0; i < dev->drivers; i++)
+		if (dev->drv[i]->locks > 0) {
+			isdn_ctrl cmd;
+
+			cmd.driver = i;
+			cmd.arg = 0;
+			cmd.command = ISDN_CMD_UNLOCK;
+			isdn_command(&cmd);
+			dev->drv[i]->locks--;
+		}
 }
 
-#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) || defined(CONFIG_ISDN_TIMEOUT_RULES)
 void
 isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
 {
@@ -329,6 +389,7 @@
 #endif
 		}
 	}
+
 	if (tf) {
 		int flags;
 
@@ -392,6 +453,15 @@
 		isdn_trash_skb(skb, FREE_READ);
 }
 
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ */
+int
+isdn_command(isdn_ctrl *cmd)
+{
+	return dev->drv[cmd->driver]->interface->command(cmd);
+}
+
 void
 isdn_all_eaz(int di, int ch)
 {
@@ -403,7 +473,7 @@
 	cmd.arg = ch;
 	cmd.command = ISDN_CMD_SETEAZ;
 	cmd.parm.num[0] = '\0';
-	(void) dev->drv[di]->interface->command(&cmd);
+	isdn_command(&cmd);
 }
 
 static int
@@ -424,7 +494,7 @@
 				return -1;
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				return 0;
 			if (isdn_tty_stat_callback(i, c))
 				return 0;
@@ -438,13 +508,13 @@
 			wake_up_interruptible(&dev->drv[di]->st_waitq);
 			break;
 		case ISDN_STAT_RUN:
-			dev->drv[di]->running = 1;
+			dev->drv[di]->flags |= DRV_FLAG_RUNNING;
 			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;
+			dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
 			break;
 		case ISDN_STAT_ICALL:
 			if (i < 0)
@@ -456,27 +526,23 @@
 				cmd.driver = di;
 				cmd.arg = c->arg;
 				cmd.command = ISDN_CMD_HANGUP;
-				dev->drv[di]->interface->command(&cmd);
+				isdn_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->parm.setup);
+			r = isdn_net_find_icall(di, c, i);
 			switch (r) {
 				case 0:
 					/* No network-device replies.
 					 * Try ttyI's
 					 */
-					if (isdn_tty_find_icall(di, c->arg, c->parm.setup) >= 0)
+					if (isdn_tty_find_icall(di, c) >= 0)
 						retval = 1;
-					else if (dev->drv[di]->reject_bus) {
+					else if (dev->drv[di]->flags & DRV_FLAG_REJBUS) {
 						cmd.driver = di;
 						cmd.arg = c->arg;
 						cmd.command = ISDN_CMD_HANGUP;
-						dev->drv[di]->interface->command(&cmd);
+						isdn_command(&cmd);
 						retval = 2;
 					}
 					break;
@@ -486,7 +552,7 @@
 					cmd.driver = di;
 					cmd.arg = c->arg;
 					cmd.command = ISDN_CMD_ACCEPTD;
-					dev->drv[di]->interface->command(&cmd);
+					isdn_command(&cmd);
 					retval = 1;
 					break;
 				case 2:	/* For calling back, first reject incoming call ... */
@@ -496,7 +562,7 @@
 					cmd.driver = di;
 					cmd.arg = c->arg;
 					cmd.command = ISDN_CMD_HANGUP;
-					dev->drv[di]->interface->command(&cmd);
+					isdn_command(&cmd);
 					if (r == 3)
 						break;
 					/* Fall through */
@@ -505,12 +571,6 @@
 					isdn_net_dial();
 					break;
 			}
-			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:
@@ -522,7 +582,7 @@
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
 			if (strcmp(c->parm.num, "0"))
-				isdn_net_stat_callback(i, c->command);
+				isdn_net_stat_callback(i, c);
 			break;
 		case ISDN_STAT_CAUSE:
 #ifdef ISDN_DEBUG_STATCALLB
@@ -541,14 +601,14 @@
 			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))
+			if (isdn_net_stat_callback(i, c))
 				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);
+				isdn_command(&cmd);
 				break;
 			}
 			break;
@@ -560,10 +620,10 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags &= ~(1 << (c->arg));
+			dev->drv[di]->online &= ~(1 << (c->arg));
 			isdn_info_update();
 			/* Signal hangup to network-devices */
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -577,9 +637,9 @@
 			/* Signal B-channel-connect to network-devices */
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags |= (1 << (c->arg));
+			dev->drv[di]->online |= (1 << (c->arg));
 			isdn_info_update();
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -592,7 +652,7 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			dev->drv[di]->flags &= ~(1 << (c->arg));
+			dev->drv[di]->online &= ~(1 << (c->arg));
 			isdn_info_update();
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -605,7 +665,7 @@
 #endif
 			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 				return 0;
-			if (isdn_net_stat_callback(i, c->command))
+			if (isdn_net_stat_callback(i, c))
 				break;
 			if (isdn_tty_stat_callback(i, c))
 				break;
@@ -817,7 +877,7 @@
 	p = istatbuf + strlen(istatbuf);
 	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
 		if (dev->drv[i]) {
-			sprintf(p, "%ld ", dev->drv[i]->flags);
+			sprintf(p, "%ld ", dev->drv[i]->online);
 			p = istatbuf + strlen(istatbuf);
 		} else {
 			sprintf(p, "? ");
@@ -880,7 +940,7 @@
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
 			return -ENODEV;
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
 		save_flags(flags);
@@ -943,7 +1003,7 @@
 		drvidx = isdn_minor2drv(minor);
 		if (drvidx < 0)
 			return -ENODEV;
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
 		while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count)
@@ -957,7 +1017,7 @@
 		/*
 		 * We want to use the isdnctrl device to load the firmware
 		 *
-		 if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 		 return -ENODEV;
 		 */
 		if (dev->drv[drvidx]->interface->writecmd)
@@ -1044,136 +1104,13 @@
 }
 #endif
 
-static int
-isdn_set_allcfg(char *src)
-{
-	int ret;
-	int i;
-	ulong flags;
-	isdn_net_ioctl_cfg cfg;
+typedef union {
+	char name[10];
+	char bname[22];
+	isdn_ioctl_struct iocts;
 	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();
-	src += sizeof(int);
-	while (i) {
-		int phone_len;
-		int out_flag;
-
-		if ((ret = copy_from_user((char *) &cfg, src, sizeof(cfg)))) {
-			restore_flags(flags);
-			return ret;
-		}
-		src += sizeof(cfg);
-		if (!isdn_net_new(cfg.name, NULL)) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if ((ret = isdn_net_setcfg(&cfg))) {
-			restore_flags(flags);
-			return ret;
-		}
-		phone_len = out_flag = 0;
-		while (out_flag < 2) {
-			if ((ret = verify_area(VERIFY_READ, src, 1))) {
-				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 (++phone_len >= sizeof(phone.phone))
-				printk(KERN_WARNING
-				       "%s: IIOCSETSET phone number too long, ignored\n",
-				       cfg.name);
-		}
-		i--;
-	}
-	restore_flags(flags);
-	return 0;
-}
-
-static int
-isdn_get_allcfg(char *dest)
-{
 	isdn_net_ioctl_cfg cfg;
-	isdn_net_ioctl_phone phone;
-	isdn_net_dev *p;
-	ulong flags;
-	int ret;
-
-	/* Walk through netdev-chain */
-	save_flags(flags);
-	cli();
-	p = dev->netdev;
-	while (p) {
-		if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) {
-			restore_flags(flags);
-			return ret;
-		}
-		strcpy(cfg.eaz, p->local.msn);
-		cfg.exclusive = p->local.exclusive;
-		if (p->local.pre_device >= 0) {
-			sprintf(cfg.drvid, "%s,%d", dev->drvid[p->local.pre_device],
-				p->local.pre_channel);
-		} else
-			cfg.drvid[0] = '\0';
-		cfg.onhtime = p->local.onhtime;
-		cfg.charge = p->local.charge;
-		cfg.l2_proto = p->local.l2_proto;
-		cfg.l3_proto = p->local.l3_proto;
-		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 & 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;
-		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;
-		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
-			restore_flags(flags);
-			return ret;
-		} else
-			dest += ret;
-		strcpy(phone.name, p->local.name);
-		phone.outgoing = 1;
-		if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
-			restore_flags(flags);
-			return ret;
-		} else
-			dest += ret;
-		put_user(0, dest);
-		p = p->next;
-	}
-	restore_flags(flags);
-	return 0;
-}
+} iocpar_t;
 
 static int
 isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
@@ -1186,19 +1123,15 @@
 	int i;
 	char *p;
 	char *s;
-	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
+	iocpar_t *iocpar;
+
+#define name  iocpar->name
+#define bname iocpar->bname
+#define iocts iocpar->iocts
+#define phone iocpar->phone
+#define cfg   iocpar->cfg
+
+#define RETURN(r) ({ ret = r; kfree(iocpar); return ret; })
 
 	if (minor == ISDN_MINOR_STATUS) {
 		switch (cmd) {
@@ -1232,127 +1165,131 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
 		return 0;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
+		if ((iocpar = (iocpar_t *)kmalloc(sizeof(iocpar_t), GFP_KERNEL)) == NULL) {
+			printk(KERN_WARNING "isdn: Out of memory in isdn_ioctl\n");
+			return -ENOMEM;
+		}
 		switch (cmd) {
 #ifdef CONFIG_NETDEVICES
 			case IIOCNETAIF:
 				/* Add a network-interface */
 				if (arg) {
 					if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-						return ret;
+						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;
+						RETURN(ret);
+					RETURN(0);
 				} else
-					return -ENODEV;
+					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;
+						RETURN(ret);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if ((s = isdn_net_newslave(bname))) {
 					if ((ret = copy_to_user((char *) arg, s, strlen(s) + 1)))
-						return ret;
-					return 0;
+						RETURN(ret);
+					RETURN(0);
 				} else
-					return -ENODEV;
+					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);
+						RETURN(ret);
+					RETURN(isdn_net_rm(name));
 				} else
-					return -EINVAL;
+					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);
+						RETURN(ret);
+					RETURN(isdn_net_setcfg(&cfg));
 				} else
-					return -EINVAL;
+					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;
+						RETURN(ret);
 					if (!(ret = isdn_net_getcfg(&cfg))) {
 						if ((ret = copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg))))
-							return ret;
+							RETURN(ret);
 					}
-					return ret;
+					RETURN(ret);
 				} else
-					return -EINVAL;
+					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);
+						RETURN(ret);
+					RETURN(isdn_net_addphone(&phone));
 				} else
-					return -EINVAL;
+					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);
+						RETURN(ret);
+					RETURN(isdn_net_getphones(&phone, (char *) arg));
 				} else
-					return -EINVAL;
+					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);
+						RETURN(ret);
+					RETURN(isdn_net_delphone(&phone));
 				} else
-					return -EINVAL;
+					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);
+						RETURN(ret);
+					RETURN(isdn_net_force_dial(name));
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 #ifdef CONFIG_ISDN_PPP
 			case IIOCNETALN:
 				if (!arg)
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-					return ret;
-				return isdn_ppp_dial_slave(name);
+					RETURN(ret);
+				RETURN(isdn_ppp_dial_slave(name));
 			case IIOCNETDLN:
 				if (!arg)
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-					return ret;
-				return isdn_ppp_hangup_slave(name);
+					RETURN(ret);
+				RETURN(isdn_ppp_hangup_slave(name));
 #endif
 			case IIOCNETHUP:
 				/* Force hangup of a network-interface */
 				if (!arg)
-					return -EINVAL;
+					RETURN(-EINVAL);
 			        if ((ret = copy_from_user(name, (char *) arg, sizeof(name))))
-			        	return ret;
-			        return isdn_net_force_hangup(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;
+				RETURN(0);
 			case IIOCSETGST:
 				if (arg)
 					dev->global_flags |= ISDN_GLOBAL_STOPPED;
@@ -1360,7 +1297,7 @@
 					dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
 				printk(KERN_INFO "isdn: Global Mode %s\n",
 				       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
-				return 0;
+				RETURN(0);
 			case IIOCSETBRJ:
 				drvidx = -1;
 				if (arg) {
@@ -1368,7 +1305,7 @@
 					char *p;
 					if ((ret = copy_from_user((char *) &iocts, (char *) arg,
 					      sizeof(isdn_ioctl_struct))))
-						return ret;
+						RETURN(ret);
 					if (strlen(iocts.drvid)) {
 						if ((p = strchr(iocts.drvid, ',')))
 							*p = 0;
@@ -1381,28 +1318,15 @@
 					}
 				}
 				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));
+					RETURN(-ENODEV);
+				if (iocts.arg)
+					dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
 				else
-					return -EINVAL;
-				break;
+					dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
+				RETURN(0);
 			case IIOCSIGPRF:
 				dev->profd = current;
-				return 0;
+				RETURN(0);
 				break;
 			case IIOCGETPRF:
 				/* Get all Modem-Profiles */
@@ -1413,20 +1337,20 @@
 					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
 					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
 						   * ISDN_MAX_CHANNELS)))
-						return ret;
+						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;
+							RETURN(-EFAULT);
 						p += ISDN_MODEM_ANZREG;
 						if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
-							return -EFAULT;
+							RETURN(-EFAULT);
 						p += ISDN_MSNLEN;
 					}
-					return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+					RETURN((ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				break;
 			case IIOCSETPRF:
 				/* Set all Modem-Profiles */
@@ -1437,20 +1361,20 @@
 					if ((ret = verify_area(VERIFY_READ, (void *) arg,
 					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
 						   * ISDN_MAX_CHANNELS)))
-						return ret;
+						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;
+							RETURN(ret);
 						p += ISDN_MODEM_ANZREG;
 						if ((ret = copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)))
-							return ret;
+							RETURN(ret);
 						p += ISDN_MSNLEN;
 					}
-					return 0;
+					RETURN(0);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				break;
 			case IIOCSETMAP:
 			case IIOCGETMAP:
@@ -1460,7 +1384,7 @@
 					if ((ret = copy_from_user((char *) &iocts,
 							    (char *) arg,
 					     sizeof(isdn_ioctl_struct))))
-						return ret;
+						RETURN(ret);
 					if (strlen(iocts.drvid)) {
 						drvidx = -1;
 						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
@@ -1471,7 +1395,7 @@
 					} else
 						drvidx = 0;
 					if (drvidx == -1)
-						return -ENODEV;
+						RETURN(-ENODEV);
 					if (cmd == IIOCSETMAP) {
 						int loop = 1;
 
@@ -1482,7 +1406,7 @@
 
 							while (1) {
 								if ((ret = verify_area(VERIFY_READ, p, 1)))
-									return ret;
+									RETURN(ret);
 								GET_USER(bname[j], p++);
 								switch (bname[j]) {
 									case '\0':
@@ -1510,31 +1434,31 @@
 								dev->drv[drvidx]->msn2eaz[i] : "-",
 								(i < 9) ? "," : "\0");
 							if ((ret = copy_to_user(p, bname, strlen(bname) + 1)))
-								return ret;
+								RETURN(ret);
 							p += strlen(bname);
 						}
 					}
-					return 0;
+					RETURN(0);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 			case IIOCDBGVAR:
 				if (arg) {
 					if ((ret = copy_to_user((char *) arg, (char *) &dev, sizeof(ulong))))
-						return ret;
-					return 0;
+						RETURN(ret);
+					RETURN(0);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				break;
 			default:
 				if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
 					cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
 				else
-					return -EINVAL;
+					RETURN(-EINVAL);
 				if (arg) {
 					int i;
 					char *p;
 					if ((ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct))))
-						return ret;
+						RETURN(ret);
 					if (strlen(iocts.drvid)) {
 						if ((p = strchr(iocts.drvid, ',')))
 							*p = 0;
@@ -1547,21 +1471,21 @@
 					} else
 						drvidx = 0;
 					if (drvidx == -1)
-						return -ENODEV;
+						RETURN(-ENODEV);
 					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
 					     sizeof(isdn_ioctl_struct))))
-						return ret;
+						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);
+					ret = isdn_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;
+						RETURN(-EFAULT);
+					RETURN(ret);
 				} else
-					return -EINVAL;
+					RETURN(-EINVAL);
 		}
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1575,6 +1499,8 @@
 #undef iocts
 #undef phone
 #undef cfg
+
+#undef RETURN
 }
 
 /*
@@ -1588,7 +1514,6 @@
 	uint minor = MINOR(ino->i_rdev);
 	int drvidx;
 	int chidx;
-	isdn_ctrl c;
 
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p;
@@ -1611,31 +1536,25 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-		if (!dev->drv[drvidx]->running)
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
 			return -ENODEV;
-		if (!(dev->drv[drvidx]->flags & (1 << chidx)))
+		if (!(dev->drv[drvidx]->online & (1 << chidx)))
 			return -ENODEV;
-		c.command = ISDN_CMD_LOCK;
-		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
-		MOD_INC_USE_COUNT;
+		isdn_MOD_INC_USE_COUNT();
 		return 0;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 		if (drvidx < 0)
 			return -ENODEV;
-		c.command = ISDN_CMD_LOCK;
-		c.driver = drvidx;
-		MOD_INC_USE_COUNT;
-		(void) dev->drv[drvidx]->interface->command(&c);
+		isdn_MOD_INC_USE_COUNT();
 		return 0;
 	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX) {
 		int ret;
 		if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
-			MOD_INC_USE_COUNT;
+			isdn_MOD_INC_USE_COUNT();
 		return ret;
 	}
 #endif
@@ -1646,13 +1565,13 @@
 isdn_close(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
-	int drvidx;
-	isdn_ctrl c;
 
-	MOD_DEC_USE_COUNT;
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p = dev->infochain;
 		infostruct *q = NULL;
+
+
+		MOD_DEC_USE_COUNT;
 		while (p) {
 			if (p->private == (char *) &(filep->private_data)) {
 				if (q)
@@ -1668,24 +1587,12 @@
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
 		return CLOSEVAL;
 	}
-	if (minor < ISDN_MINOR_CTRL) {
-		drvidx = isdn_minor2drv(minor);
-		if (drvidx < 0)
-			return CLOSEVAL;
-		c.command = ISDN_CMD_UNLOCK;
-		c.driver = drvidx;
-		(void) dev->drv[drvidx]->interface->command(&c);
+	isdn_MOD_DEC_USE_COUNT();
+	if (minor < ISDN_MINOR_CTRL)
 		return CLOSEVAL;
-	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
-		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-		if (drvidx < 0)
-			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 CLOSEVAL;
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1739,7 +1646,6 @@
 	int i;
 	ulong flags;
 	ulong features;
-	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
@@ -1751,16 +1657,12 @@
 			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
 			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
 				continue;
-			if ((dev->drv[d]->running)) {
+			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
 				if ((dev->drv[d]->interface->features & features) == features) {
 					if ((pre_dev < 0) || (pre_chan < 0)) {
 						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);
 						restore_flags(flags);
 						return i;
 					} else {
@@ -1768,10 +1670,6 @@
 							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);
 							restore_flags(flags);
 							return i;
 						}
@@ -1807,11 +1705,12 @@
 			isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
 			cmd.driver = di;
 			cmd.arg = ch;
-			cmd.command = ISDN_CMD_UNLOCK;
+			cmd.command = ISDN_CMD_HANGUP;
 			restore_flags(flags);
-			(void) dev->drv[di]->interface->command(&cmd);
+			isdn_command(&cmd);
 			return;
 		}
+	printk(KERN_DEBUG "Unmatched free_channel d=%d c=%d u=%d\n", di, ch, usage);
 	restore_flags(flags);
 }
 
@@ -2007,12 +1906,11 @@
 	}
 	memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * n);
 	d->channels = n;
-	d->loaded = 1;
 	d->maxbufsize = i->maxbufsize;
 	d->pktcount = 0;
 	d->stavail = 0;
-	d->running = 0;
-	d->flags = 0;
+	d->flags = DRV_FLAG_LOADED;
+	d->online = 0;
 	d->interface = i;
 	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
 		if (!dev->drv[drvidx])
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- v2.0.35/linux/drivers/isdn/isdn_common.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_common.h	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_common.h,v 1.6 1997/02/28 02:32:44 fritz Exp $
+/* $Id: isdn_common.h,v 1.6.2.3 1998/11/05 22:11:50 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 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -21,6 +21,15 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.h,v $
+ * Revision 1.6.2.3  1998/11/05 22:11:50  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.6.2.2  1998/10/25 15:48:10  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
+ * Revision 1.6.2.1  1998/03/16 09:55:48  cal
+ * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches.
+ *
  * Revision 1.6  1997/02/28 02:32:44  fritz
  * Cleanup: Moved some tty related stuff from isdn_common.c
  *          to isdn_tty.c
@@ -60,6 +69,7 @@
 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 int isdn_command(isdn_ctrl *);
 extern void isdn_all_eaz(int di, int ch);
 extern int isdn_dc2minor(int di, int ch);
 extern void isdn_info_update(void);
@@ -76,6 +86,6 @@
 #else
 #define isdn_export_syms()
 #endif
-#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) || defined(CONFIG_ISDN_TIMEOUT_RULES)
 extern void isdn_dumppkt(char *, u_char *, int, int);
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.0.35/linux/drivers/isdn/isdn_net.c	Sun Nov 15 10:49:35 1998
+++ linux/drivers/isdn/isdn_net.c	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_net.c,v 1.47 1997/06/21 10:52:05 fritz Exp $
+/* $Id: isdn_net.c,v 1.48.2.27 1998/11/05 22:11:53 fritz Exp $
 
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -21,6 +21,135 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.48.2.27  1998/11/05 22:11:53  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.48.2.26  1998/11/03 14:54:39  fritz
+ * Applied callback-patch fur bundled RAW-IP by gvz@popocate.hamburg.pop.de
+ *
+ * Revision 1.48.2.25  1998/11/03 14:31:05  fritz
+ * Reduced stack usage in various functions.
+ * Adapted statemachine to work with certified HiSax.
+ * Some fixes in callback handling.
+ *
+ * Revision 1.48.2.24  1998/10/25 22:08:22  fritz
+ * Bugfix: Only first number was dialed.
+ *
+ * Revision 1.48.2.23  1998/10/25 17:42:18  fritz
+ * Bugfix: added missing reset of connect-flag.
+ *
+ * Revision 1.48.2.22  1998/10/25 15:48:13  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
+ * Revision 1.48.2.21  1998/10/23 10:14:02  paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.48.2.20  1998/08/03 15:52:00  paul
+ * various changes from 2.0.3[45] kernel sources, as suggested by
+ * Oliver.Lauer@coburg.baynet.de
+ *
+ * Revision 1.48.2.19  1998/07/30 11:29:32  paul
+ * printk message only appeared when status is off and interface is rawIP,
+ * which is confusing for people who don't know about "isdnctrl status <if> on".
+ *
+ * Revision 1.48.2.18  1998/06/29 17:08:20  cal
+ * applied small TimRu-patch by Oliver Lauer
+ *
+ * Revision 1.48.2.17  1998/06/26 22:00:47  keil
+ * tx_queue_len = 5 was too small
+ *
+ * Revision 1.48.2.16  1998/06/09 12:24:40  cal
+ * Changed default of local netdev flags: ISDN_NET_STOPPED is default now,
+ * so autodial is suppressed for that device until it is switched on using
+ * 'isdnctrl status dev-name on'.
+ *
+ * Revision 1.48.2.15  1998/06/07 13:47:51  fritz
+ * ABC cleanup
+ *
+ * Revision 1.48.2.13  1998/05/22 10:13:07  detabc
+ * in case of a icmp-unreach condition the tcp-keepalive-entrys
+ * will be dropped from the internal double-link-list (only abc-extension).
+ * send icmp unreach only if the skb->protocol == ETH_P_IP
+ *
+ * Revision 1.48.2.12  1998/05/21 09:23:56  detabc
+ * speedup abc-no-dchannel-redial
+ *
+ * Revision 1.48.2.11  1998/05/07 19:54:53  detabc
+ * bugfix in abc_delayed_hangup
+ * optimize keepalive-tests for abc_rawip
+ *
+ * Revision 1.48.2.10  1998/05/06 08:34:04  detabc
+ * change ICMP_HOST_UNREACH to ICMP_NET_UNREACH (only abc-ext.)
+ * set dev->tbusy to zero in isdn_net_unreachable() (only abc-ext.)
+ * drop all new packets and send ICMP_NET_UNREACH for
+ * min. dialwait to max. dialwait * 6 time. (only abc-ext.)
+ * change random-deliver of packets (small first) from all emcapsulation
+ * to only rawip with ABC-Router-Flag enabled.
+ *
+ * Revision 1.48.2.9  1998/05/03 17:48:22  detabc
+ * remove unused dev->tbusy = 1 line (only abc-extension)
+ *
+ * Revision 1.48.2.8  1998/04/28 15:11:55  detabc
+ * fixed the wrong #ifndef CONFIG_ISDN_WITH_ABC define
+ *
+ * Revision 1.48.2.7  1998/04/26 11:24:08  detabc
+ * add abc_delayed_hangup (only with a spezial udp-packet)
+ * move abc-compress and -crypt from start of transmit to the
+ * isdn_net_send_skb() function (better for TIMRU and the work is much easyer).
+ *
+ * added the abc_tx_queue's in the isdn_net_send_skb().
+ * give small-packets a high priority.
+ * transmit small packest first.
+ * NOTE: NOTE: NOTE:
+ * At now with the ABC-EXTENSION will be deliver the pakets in RANDOM-ORDER.
+ * Please let me know if this a problem.
+ *
+ * Revision 1.48.2.6  1998/04/18 17:55:09  detabc
+ * dropp packets if call's are disabled (only abc-extension)
+ * add secure callback (only abc-extension)
+ * this means: if you are the callback-out-side and the remote
+ * dont reject the call ?????
+ * in this case the connection is ok !!! but you pay the connection !!!!
+ * now this will be a configerror and the connection will be dropped .
+ * also a new call will be disabled for 4 hours.
+ * incouming-calls are still possible.
+ *
+ * Revision 1.48.2.5  1998/04/16 19:24:51  keil
+ * Fix from vger (tx max qlength)
+ *
+ * Revision 1.48.2.4  1998/03/20 12:17:27  detabc
+ * merge abc-extension with timru-time-rules
+ * christian please check my changes in the CONFIG_ISDN_TIMEOUT_RULES sources
+ * please ! think about:
+ * behind the function isdn_abc_net_start_xmit(), is the first one behind
+ * the kernel-driver, the paket will be compressed an/or crypted. In this
+ * case no information availible in the skb->data area.
+ *
+ * Fritz !! Please read my remarks in the funktion isdn_net_unreachable() !
+ *
+ * Revision 1.48.2.3  1998/03/16 09:55:51  cal
+ * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches.
+ *
+ * Revision 1.48.2.2  1998/03/07 23:35:09  detabc
+ * added the abc-extension to the linux isdn-kernel
+ * for kernel-version 2.0.xx
+ * DO NOT USE FOR HIGHER KERNELS-VERSIONS
+ * all source-lines are switched with the define  CONFIG_ISDN_WITH_ABC
+ * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes
+ *
+ * you need also a modified isdnctrl-source the switch on the
+ * features of the abc-extension
+ *
+ * please use carefully. more detail will be follow.
+ * thanks
+ *
+ * Revision 1.48.2.1  1997/08/21 15:56:07  fritz
+ * Synchronized 2.0.X branch with 2.0.31-pre7
+ *
+ * Revision 1.48  1997/06/22 11:57:15  fritz
+ * Added ability to adjust slave triggerlevel.
+ *
  * Revision 1.47  1997/06/21 10:52:05  fritz
  * Removed wrong SET_SKB_FREE in isdn_net_send_skb()
  *
@@ -30,6 +159,7 @@
  * 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
@@ -208,7 +338,9 @@
 #include <net/icmp.h>
 #include "isdn_common.h"
 #include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
 #include "isdn_ppp.h"
+#endif
 
 /* Prototypes */
 
@@ -218,7 +350,7 @@
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
 static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
 
-char *isdn_net_revision = "$Revision: 1.47 $";
+char *isdn_net_revision = "$Revision: 1.48.2.27 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -227,14 +359,46 @@
 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);
-	if(skb->protocol==htons(ETH_P_IP))
-		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
+	int i;
+
+	if(skb != NULL) {
+	
+		u_short proto = ntohs(skb->protocol);
+
+		printk(KERN_DEBUG "isdn_net: %s: %s, send ICMP %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown",
+			   (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "" );
+
+		if(proto == ETH_P_IP) {
+
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN, 0
+#if (LINUX_VERSION_CODE < 0x02010f)	/* 2.1.15 */
+				,dev
+#endif
+				);
+		}
+	}
+	else {  /* dial not triggered by rawIP packet */
+		printk(KERN_DEBUG "isdn_net: %s: %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown");
+	}
+
+	for(i = 0; i < DEV_NUMBUFFS; i++) {
+		struct sk_buff *skb;
+
+		while((skb = skb_dequeue(&dev->buffs[i]))) {
+				if(ntohs(skb->protocol) == ETH_P_IP) {
+					icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0
 #if (LINUX_VERSION_CODE < 0x02010f)	/* 2.1.15 */
-		  ,dev
+					, dev
 #endif
-	    );
+					);
+				}
+				dev_kfree_skb(skb, FREE_WRITE);
+        	}
+	}
 }
 
 static void
@@ -293,6 +457,21 @@
 	restore_flags(flags);
 }
 
+static inline void
+isdn_net_unbind_ptr_idx(int idx)
+{
+	if (idx != -1) {
+		dev->rx_netdev[idx] = NULL;
+		dev->st_netdev[idx] = NULL;
+	}
+}
+
+static inline void
+isdn_net_unbind_ptr(int drv, int ch)
+{
+	isdn_net_unbind_ptr_idx(isdn_dc2minor(drv, ch));
+}
+
 /*
  * unbind a net-interface (resets interface after an error)
  */
@@ -314,9 +493,10 @@
 	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;
-	isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+	if (lp->isdn_device != -1 && lp->isdn_device != -1) {
+		isdn_net_unbind_ptr(lp->isdn_device, lp->isdn_channel);
+		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;
@@ -358,7 +538,13 @@
 		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
 			anymore = 1;
 			l->huptimer++;
-			if ((l->onhtime) && (l->huptimer > l->onhtime))
+ 			/*
+ 			 * only do timeout-hangup
+ 			 * if interface is configured as AUTO
+ 			 */
+  			if ((ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_AUTO) &&
+ 			    (l->onhtime) &&
+ 			    (l->huptimer > l->onhtime))
 				if (l->hupflags & ISDN_MANCHARGE &&
 				    l->hupflags & ISDN_CHARGEHUP) {
 					while (jiffies - l->chargetime > l->chargeint)
@@ -382,6 +568,12 @@
 						isdn_net_hangup(&p->dev);
 				} else if (l->hupflags & ISDN_INHUP)
 					isdn_net_hangup(&p->dev);
+
+
+			if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(p->local) == ISDN_NET_DM_OFF)) {
+				isdn_net_hangup(&p->dev);
+				break;
+			}
 		}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -396,12 +588,14 @@
  * Return: 1 = Event handled, 0 = not for us or unknown Event.
  */
 int
-isdn_net_stat_callback(int idx, int cmd)
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
 {
 	isdn_net_dev *p = dev->st_netdev[idx];
 
 	if (p) {
 		isdn_net_local *lp = &(p->local);
+		int cmd = c->command;
+
 		switch (cmd) {
 			case ISDN_STAT_BSENT:
 				/* A packet has successfully been sent out */
@@ -434,35 +628,55 @@
 						lp->dialstate++;
 						return 1;
 					case 12:
+					case 13:
 						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);
-						lp->first_skb = NULL;
-					}
-					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);
+				if (lp->flags & ISDN_NET_CONNECTED) {
+					printk(KERN_INFO "%s: remote %s (%d)\n", lp->name,
+						lp->dialstate?"abort":"hangup", lp->dialstate);
+					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
+						lp->charge);
+printk(KERN_DEBUG "idx=%d drv=%d ch=%d\n",idx, lp->isdn_device, lp->isdn_channel);
+					if (!lp->dialstate) {
+						isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 #ifdef CONFIG_ISDN_PPP
-					isdn_ppp_free(lp);
+						isdn_ppp_free(lp);
 #endif
-					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
-					printk(KERN_INFO "%s: remote hangup\n", lp->name);
-					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
-					       lp->charge);
-					lp->isdn_device = -1;
-					lp->isdn_channel = -1;
-					dev->st_netdev[idx] = NULL;
-					dev->rx_netdev[idx] = NULL;
+						isdn_net_unbind_channel(lp);
+					} else {
+						switch (lp->dialstate) {
+							case 4:
+							case 5:
+							case 6:
+							case 7:
+							case 8:
+							case 9:
+								lp->dialstate = 3;
+								break;
+							case 11:
+								break;
+							case 12:
+								{
+									isdn_ctrl cmd;
+									printk(KERN_INFO "%s: got reject, waiting for callback ...\n", p->local.name);
+
+									p->local.dtimer = 0;
+									p->local.dialstate = 13;
+									cmd.driver = p->local.isdn_device;
+									cmd.command = ISDN_CMD_HANGUP;
+									cmd.arg = p->local.isdn_channel;
+									isdn_command(&cmd);
+									isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
+								}
+								/* Fall through */
+							case 13:
+								break;
+						}
+					}
 					return 1;
 				}
 				break;
@@ -568,6 +782,7 @@
 		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 */
@@ -587,6 +802,13 @@
 					break;
 				}
 				anymore = 1;
+
+				if(p->local.dialtimeout > 0)
+					if(p->local.dialstarted == 0 || jiffies > (p->local.dialstarted + p->local.dialtimeout + p->local.dialwait)) {
+						p->local.dialstarted = jiffies;
+						p->local.dialwait_timer = 0;
+					}
+
 				p->local.dialstate++;
 				/* Fall through */
 			case 2:
@@ -594,27 +816,37 @@
 				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);
+				isdn_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);
+				isdn_command(&cmd);
 				p->local.dialretry = 0;
 				anymore = 1;
 				p->local.dialstate++;
-				/* Falls through */
+				/* Fall through */
 			case 3:
 				/* Setup interface, dial current phone-number, switch to next number.
 				 * If list of phone-numbers is exhausted, increment
 				 * retry-counter.
 				 */
+				if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(p->local) == ISDN_NET_DM_OFF)) {
+ 					char *s;
+ 					if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ 						s = "dial suppressed: isdn system stopped";
+ 					else
+ 						s = "dial suppressed: dialmode `off'";
+					isdn_net_unreachable(&p->dev, p->local.first_skb, s);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
 				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);
+				isdn_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);
+				isdn_command(&cmd);
 				cmd.driver = p->local.isdn_device;
 				cmd.arg = p->local.isdn_channel;
 				save_flags(flags);
@@ -631,6 +863,16 @@
 					p->local.dialstate = 4;
 					printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
 				} else {
+					if(p->local.dialtimeout > 0)
+						if(jiffies > (p->local.dialstarted + p->local.dialtimeout)) {
+							restore_flags(flags);
+							p->local.dialwait_timer = jiffies + p->local.dialwait;
+							p->local.dialstarted = 0;
+							isdn_net_unreachable(&p->dev, p->local.first_skb, "dial: timed out");
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+
 					sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num);
 					/*
 					 * Switch to next number or back to start if at end of list.
@@ -638,6 +880,17 @@
 					if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
 						p->local.dial = p->local.phone[1];
 						p->local.dialretry++;
+
+						if (p->local.dialretry > p->local.dialmax) {
+							restore_flags(flags);
+							if (p->local.dialtimeout == 0) {
+								p->local.dialwait_timer = jiffies + p->local.dialwait;
+								p->local.dialstarted = 0;
+								isdn_net_unreachable(&p->dev, p->local.first_skb, "dial: tried all numbers dialmax times");
+							}
+							isdn_net_hangup(&p->dev);
+							break;
+						}
 					}
 					restore_flags(flags);
 					cmd.command = ISDN_CMD_DIAL;
@@ -651,13 +904,14 @@
 						isdn_info_update();
 					}
 					printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
-					       p->local.dialretry - 1, cmd.parm.setup.phone);
+					       p->local.dialretry, 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);
 #endif
-					dev->drv[p->local.isdn_device]->interface->command(&cmd);
+					cmd.driver = p->local.isdn_device;
+					isdn_command(&cmd);
 				}
 				p->local.huptimer = 0;
 				p->local.outgoing = 1;
@@ -669,20 +923,19 @@
 					p->local.hupflags &= ~ISDN_HAVECHARGE;
 				}
 				anymore = 1;
-				p->local.dialstate =
-				    (p->local.cbdelay &&
-				     (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4;
+				if (p->local.flags & ISDN_NET_CBOUT) {
+					p->local.dialstate = (p->local.cbdelay) ? 12 : 13;
+				} else
+					p->local.dialstate = 4;
 				break;
 			case 4:
+			case 13:
 				/* Wait for D-Channel-connect.
-				 * If timeout and max retries not
-				 * reached, switch back to state 3.
+				 * If timeout, switch back to state 3.
+				 * Dialmax-handling moved 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);
+					p->local.dialstate = 3;
 				anymore = 1;
 				break;
 			case 5:
@@ -693,11 +946,12 @@
 				anymore = 1;
 				p->local.dtimer = 0;
 				p->local.dialstate++;
-				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				isdn_command(&cmd);
 				break;
 			case 6:
 				/* Wait for B- or D-Channel-connect. If timeout,
 				 * switch back to state 3.
+				 * Dialmax-handling moved to state 3.
 				 */
 #ifdef ISDN_DEBUG_NET_DIAL
 				printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
@@ -716,11 +970,12 @@
 				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);
+				isdn_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);
+				isdn_command(&cmd);
+
 				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
 					isdn_net_hangup(&p->dev);
 				else {
@@ -733,7 +988,7 @@
 				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);
+				isdn_command(&cmd);
 				anymore = 1;
 				p->local.dtimer = 0;
 				p->local.dialstate++;
@@ -744,7 +999,7 @@
 #ifdef ISDN_DEBUG_NET_DIAL
 				printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
 #endif
-				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT60)
 					isdn_net_hangup(&p->dev);
 				else
 					anymore = 1;
@@ -757,16 +1012,17 @@
 				break;
 			case 12:
 				/* Remote does callback. Hangup after cbdelay, then wait for incoming
-				 * call (in state 4).
+				 * call (in state 13).
 				 */
-				if (p->local.dtimer++ > p->local.cbdelay) {
+				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;
+					p->local.dialstate = 13;
 					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_command(&cmd);
 					isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
 				}
 				anymore = 1;
@@ -795,12 +1051,14 @@
 #ifdef CONFIG_ISDN_PPP
 		isdn_ppp_free(lp);
 #endif
-		cmd.driver = lp->isdn_device;
-		cmd.command = ISDN_CMD_HANGUP;
-		cmd.arg = lp->isdn_channel;
-		(void) dev->drv[cmd.driver]->interface->command(&cmd);
+		if ((lp->isdn_device != -1) && (lp->isdn_channel != -1)) {
+			cmd.driver = lp->isdn_device;
+			cmd.command = ISDN_CMD_HANGUP;
+			cmd.arg = lp->isdn_channel;
+			isdn_command(&cmd);
+			isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+		}
 		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);
 }
@@ -821,9 +1079,6 @@
 
 	addinfo[0] = '\0';
 	switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_SYNCPPP:
-                        p = &buf[IPPP_MAX_HEADER];
-                        break;
 		case ISDN_NET_ENCAP_IPTYP:
 			proto = ntohs(*(unsigned short *) &buf[0]);
 			p = &buf[2];
@@ -836,6 +1091,12 @@
 			proto = ntohs(*(unsigned short *) &buf[2]);
 			p = &buf[4];
 			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/* jump over fake header. */
+			p = &buf[IPPP_MAX_HEADER];
+			break;
+#endif
 	}
 	data_ofs = ((p[0] & 15) * 4);
 	switch (proto) {
@@ -913,7 +1174,6 @@
 	return 1;
 }
 
-
 /*
  *  Helper function for isdn_net_start_xmit.
  *  When called, the connection is already established.
@@ -994,8 +1254,10 @@
 	if (ndev->tbusy) {
 		if (jiffies - ndev->trans_start < (2 * HZ))
 			return 1;
+
 		if (!lp->dialstate)
 			lp->stats.tx_errors++;
+
 		ndev->tbusy = 0;
 		ndev->trans_start = jiffies;
 	}
@@ -1015,17 +1277,53 @@
 #endif
 		if (!(lp->flags & ISDN_NET_CONNECTED)) {
 			int chi;
+			/* only do autodial if allowed by config */
+			if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
+				isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
+				dev_kfree_skb(skb, FREE_WRITE);
+				ndev->tbusy = 0;
+				return 0;
+			}
 			if (lp->phone[1]) {
 				ulong flags;
 				save_flags(flags);
 				cli();
+
+				if(lp->dialwait_timer <= 0)
+					if(lp->dialstarted > 0 && lp->dialtimeout > 0 && jiffies < lp->dialstarted + lp->dialtimeout + lp->dialwait)
+						lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
+
+				if(lp->dialwait_timer > 0) {
+					if(jiffies < lp->dialwait_timer) {
+/*
+printk("reject: jiffies=%ld, started=%ld, timeout=%d, wait=%ld, timer=%ld\n", jiffies, lp->dialstarted, lp->dialtimeout, lp->dialwait, lp->dialwait_timer);
+*/
+						/*
+						printk(KERN_WARNING "isdn_net: Dial rejected %s, packet dropped\n",
+							ndev->name);
+						*/
+						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+						dev_kfree_skb(skb, FREE_WRITE);
+						ndev->tbusy = 0;
+						restore_flags(flags);
+						return 0;
+					} else
+						lp->dialwait_timer = 0;
+				}
+
 				/* Grab a free ISDN-Channel */
-				if ((chi =
+				if (((chi =
 				     isdn_get_free_channel(ISDN_USAGE_NET,
 							   lp->l2_proto,
 							   lp->l3_proto,
 							   lp->pre_device,
-						 lp->pre_channel)) < 0) {
+						 lp->pre_channel)) < 0) &&
+				    ((chi =
+					 isdn_get_free_channel(ISDN_USAGE_NET,
+							   lp->l2_proto,
+							   lp->l3_proto,
+							   lp->pre_device,
+						 lp->pre_channel^1)) < 0)) {
 					restore_flags(flags);
 #if 0
 					printk(KERN_WARNING
@@ -1045,6 +1343,7 @@
 				/* Log packet, which triggered dialing */
 				if (dev->net_verbose)
 					isdn_net_log_packet(buf, lp);
+
 				lp->dialstate = 1;
 				lp->flags |= ISDN_NET_CONNECTED;
 				/* Connect interface with channel */
@@ -1067,7 +1366,7 @@
 				 * when using encap ETHER
 				 */
 				if (lp->first_skb) {
-					printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
+					printk(KERN_DEBUG "isdn_net_start_xmit: First skb already set!\n");
 					dev_kfree_skb(lp->first_skb, FREE_WRITE);
 					lp->first_skb = NULL;
 				}
@@ -1268,7 +1567,8 @@
 			       lp->name);
 			kfree_skb(skb, FREE_READ);
 			return;
-	}
+ 	}
+
 	netif_rx(skb);
 	return;
 }
@@ -1346,11 +1646,11 @@
 	isdn_net_local *lp = dev->priv;
 	ushort len = 0;
 
-	skb->mac.raw = skb->data;
 	switch (lp->p_encap) {
 		case ISDN_NET_ENCAP_ETHER:
 			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
 			break;
+            break;
 #ifdef CONFIG_ISDN_PPP
 		case ISDN_NET_ENCAP_SYNCPPP:
 			/* stick on a fake header to keep fragmentation code happy. */
@@ -1493,6 +1793,9 @@
 	ndev->pa_mask = 0;
 	ndev->pa_alen = 4;
 
+	/* for clients with MPPP maybe higher values better */
+	ndev->tx_queue_len = 30;
+
 	for (i = 0; i < ETH_ALEN; i++)
 		ndev->broadcast[i] = 0xff;
 
@@ -1502,7 +1805,6 @@
 	/* The ISDN-specific entries in the device structure. */
 	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
@@ -1514,7 +1816,6 @@
 				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
 
 	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;
@@ -1648,9 +1949,11 @@
  *               4 = Wait cbdelay, then call back
  */
 int
-isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
+isdn_net_find_icall(int di, isdn_ctrl *c, int idx)
 {
 	char *eaz;
+	int ch = c->arg;
+	setup_parm *setup = &c->parm.setup;
 	int si1;
 	int si2;
 	int ematch;
@@ -1664,36 +1967,37 @@
 	/* Search name in netdev-chain */
 	save_flags(flags);
 	cli();
-	if (!setup.phone[0]) {
+	if (!setup->phone[0]) {
 		nr[0] = '0';
 		nr[1] = '\0';
 		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
 	} else
-		strcpy(nr, setup.phone);
-	si1 = (int) setup.si1;
-	si2 = (int) setup.si2;
-	if (!setup.eazmsn[0]) {
+		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;
+		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) */
 	if (si1 != 7) {
 		if (dev->net_verbose > 1)
 			printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
+		restore_flags(flags);
 		return 0;
 	}
 	n = (isdn_net_phone *) 0;
-	p = dev->netdev;
 	ematch = 0;
 #ifdef ISDN_DEBUG_NET_ICALL
 	printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
 	       dev->usage[idx]);
 #endif
 	swapped = 0;
-	while (p) {
+
+	for (p = dev->netdev; p; p = (isdn_net_dev *) p->next) {
 		/* If last check has triggered as binding-swap, revert it */
 		switch (swapped) {
 			case 2:
@@ -1704,18 +2008,20 @@
 				break;
 		}
 		swapped = 0;
-		if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
-			ematch = 1;
+		if (strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
+			continue;	/* next loop for next device */
+		ematch = 1;	/* EAZ matches! */
 #ifdef ISDN_DEBUG_NET_ICALL
 		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 ( (!(p->local.flags & ISDN_NET_CONNECTED) &&	/* not connected  */
+		      USG_NONE(dev->usage[idx]))		/* and ch. unused */
+ 		    ||						/* or */
+		     (((p->local.dialstate == 4) || (p->local.dialstate == 12) ||
+               (p->local.dialstate == 13)) &&	/* if dialing        */
+		       !(p->local.flags & ISDN_NET_CALLBACK))                 		/* but no callback   */
+		   ) /*if*/ {
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
 			       p->local.pre_device, p->local.pre_channel);
@@ -1752,7 +2058,6 @@
 									swapped = 1;
 								} else {
 									/* ... else iterate next device */
-									p = (isdn_net_dev *) p->next;
 									continue;
 								}
 							} else {
@@ -1774,7 +2079,6 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 								printk(KERN_DEBUG "n_fi: final check failed\n");
 #endif
-								p = (isdn_net_dev *) p->next;
 								continue;
 							}
 						}
@@ -1783,7 +2087,6 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
 #endif
-						p = (isdn_net_dev *) p->next;
 						continue;
 					}
 				}
@@ -1804,12 +2107,26 @@
 #ifdef ISDN_DEBUG_NET_ICALL
 				printk(KERN_DEBUG "n_fi: match3\n");
 #endif
-				/* Here we got an interface matched, now see if it is up.
+				/* matching interface found */
+
+				/*
+				 * Is the state STOPPED?
+				 * If so, no dialin is allowed,
+				 * so reject actively.
+				 * */
+				if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+					restore_flags(flags);
+					printk(KERN_INFO "incoming call, interface `%s' dialmode `off' -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/*
+				 * Is the interface up?
 				 * If not, reject the call actively.
 				 */
 				if (!p->dev.start) {
 					restore_flags(flags);
-					printk(KERN_INFO "%s: incoming call, if down -> rejected\n",
+					printk(KERN_INFO "incoming call, interface `%s' down -> rejected\n",
 					       lp->name);
 					return 3;
 				}
@@ -1820,7 +2137,7 @@
 					isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
 					printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
 					printk(KERN_DEBUG "master=%s\n", mlp->name);
-					if (mlp->flags & ISDN_NET_CONNECTED) {
+					if ((mlp->flags & ISDN_NET_CONNECTED) && (!mlp->dialstate)) {
 						printk(KERN_DEBUG "master online\n");
 						/* Master is online, find parent-slave (master if first slave) */
 						while (mlp->slave) {
@@ -1832,13 +2149,23 @@
 						printk(KERN_DEBUG "master offline\n");
 					/* Found parent, if it's offline iterate next device */
 					printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
-					if (!(mlp->flags & ISDN_NET_CONNECTED)) {
-						p = (isdn_net_dev *) p->next;
+					if (!(mlp->flags & ISDN_NET_CONNECTED) || mlp->dialstate) {
 						continue;
 					}
 				}
 				if (lp->flags & ISDN_NET_CALLBACK) {
 					int chi;
+					/*
+					 * Is the state MANUAL?
+					 * If so, no callback can be made,
+					 * so reject actively.
+					 * */
+					if (ISDN_NET_DIALMODE(*lp) != ISDN_NET_DM_AUTO) {
+						restore_flags(flags);
+						printk(KERN_INFO "incoming call for callback, interface `%s' dialmode not `auto' -> rejected\n",
+						       lp->name);
+						return 3;
+					}
 					printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
 					       lp->name, nr, eaz);
 					if (lp->phone[1]) {
@@ -1877,11 +2204,13 @@
 					       eaz);
 					/* if this interface is dialing, it does it probably on a different
 					   device, so free this device */
-					if ((p->local.dialstate == 4) || (p->local.dialstate == 12)) {
+					if (p->local.dialstate &&
+						((p->local.isdn_device != di) || (p->local.isdn_channel != ch))) {
 #ifdef CONFIG_ISDN_PPP
 						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 							isdn_ppp_free(lp);
 #endif
+						isdn_net_unbind_ptr(lp->isdn_device, lp->isdn_channel);
 						isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
 							 ISDN_USAGE_NET);
 					}
@@ -1913,10 +2242,9 @@
 				}
 			}
 		}
-		p = (isdn_net_dev *) p->next;
 	}
 	/* If none of configured EAZ/MSN matched and not verbose, be silent */
-	if (ematch || dev->net_verbose)
+	if (!ematch || dev->net_verbose)
 		printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
 	restore_flags(flags);
 	return 0;
@@ -2074,10 +2402,15 @@
 	netdev->local.srobin = &netdev->dev;
 	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.flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL; /* Hangup before Callback, manual dial */
 	netdev->local.cbdelay = 25;	/* Wait 5 secs before Callback */
+	netdev->local.dialtimeout = -1;  /* Infinite Dial-Timeout */
+	netdev->local.dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
+	netdev->local.dialstarted = 0;   /* Jiffies of last dial-start */
+	netdev->local.dialwait_timer = 0;  /* Jiffies of earliest next dial-start */
+
 	/* Put into to netdev-chain */
 	netdev->next = (void *) dev->netdev;
 	dev->netdev = netdev;
@@ -2225,6 +2558,8 @@
 		p->local.triggercps = cfg->triggercps;
 		p->local.slavedelay = cfg->slavedelay * HZ;
 		p->local.pppbind = cfg->pppbind;
+		p->local.dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+		p->local.dialwait    = cfg->dialwait * HZ;
 		if (cfg->secure)
 			p->local.flags |= ISDN_NET_SECURE;
 		else
@@ -2246,6 +2581,16 @@
 				p->local.flags &= ~ISDN_NET_CALLBACK;
 				break;
 		}
+ 		p->local.flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
+ 		if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+ 			/* old isdnctrl version, where only 0 or 1 is given */
+ 			printk(KERN_WARNING 
+ 			     "Old isdnctrl version detected! Please update.\n");
+ 			p->local.flags |= ISDN_NET_DM_OFF; /* turn on 'off' bit */
+ 		}
+ 		else {
+ 			p->local.flags |= cfg->dialmode;   /* turn on selected bits */
+ 		}
 		if (cfg->chargehup)
 			p->local.hupflags |= ISDN_CHARGEHUP;
 		else
@@ -2259,7 +2604,6 @@
 			p->local.chargeint = cfg->chargeint * HZ;
 		}
 		if (cfg->p_encap != p->local.p_encap) {
-			/* FIXME: What if there are alias devices too? */
 			if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
 				p->dev.hard_header = NULL;
 #if (LINUX_VERSION_CODE < 0x02010F)
@@ -2286,7 +2630,11 @@
 					p->dev.hard_header_cache = NULL;
 #endif
 					p->dev.header_cache_update = NULL;
-					p->dev.flags = IFF_NOARP | IFF_SOFTHEADERS;
+					/*
+					 * paul: IFF_SOFTHEADERS was added in
+					 * 2.0.33?? Does this make sense?
+					 */
+					p->dev.flags = IFF_NOARP /* | IFF_SOFTHEADERS */;
 				}
 			}
 		}
@@ -2324,15 +2672,18 @@
 		if (p->local.flags & ISDN_NET_CBOUT)
 			cfg->callback = 2;
 		cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0;
+ 		cfg->dialmode = p->local.flags & ISDN_NET_DIALMODE_MASK;
 		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->triggercps = p->local.triggercps;
+        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;
+		cfg->dialtimeout = p->local.dialtimeout >= 0 ? p->local.dialtimeout / HZ : -1;
+		cfg->dialwait = p->local.dialwait / HZ;
 		if (p->local.slave)
 			strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name);
 		else
@@ -2433,6 +2784,7 @@
 				else
 					p->local.phone[inout] = n->next;
 				kfree(n);
+				restore_flags(flags);
 				return 0;
 			}
 			m = n;
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- v2.0.35/linux/drivers/isdn/isdn_net.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_net.h	Sun Nov 15 10:32:59 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $
+/* $Id: isdn_net.h,v 1.5.2.2 1998/11/05 22:12:09 fritz Exp $
 
  * header for Linux ISDN subsystem, network related functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  *
@@ -21,6 +21,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.h,v $
+ * Revision 1.5.2.2  1998/11/05 22:12:09  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.5.2.1  1998/10/25 15:48:29  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
  * Revision 1.5  1997/02/10 20:12:47  fritz
  * Changed interface for reporting incoming calls.
  *
@@ -49,13 +55,13 @@
 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_stat_callback(int, isdn_ctrl *);
 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 int isdn_net_find_icall(int, isdn_ctrl *, int);
 extern void isdn_net_hangup(struct device *);
 extern void isdn_net_dial(void);
 extern void isdn_net_autohup(void);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.0.35/linux/drivers/isdn/isdn_ppp.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_ppp.c	Sun Nov 15 10:33:00 1998
@@ -1,4 +1,4 @@
-/* $Id: isdn_ppp.c,v 1.28 1997/06/17 13:05:57 hipp Exp $
+/* $Id: isdn_ppp.c,v 1.28.2.2 1998/11/03 14:31:23 fritz Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
@@ -19,6 +19,14 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.28.2.2  1998/11/03 14:31:23  fritz
+ * Reduced stack usage in various functions.
+ * Adapted statemachine to work with certified HiSax.
+ * Some fixes in callback handling.
+ *
+ * Revision 1.28.2.1  1998/03/16 09:56:02  cal
+ * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches.
+ *
  * 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)
@@ -173,7 +181,7 @@
 static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision = "$Revision: 1.28 $";
+char *isdn_ppp_revision = "$Revision: 1.28.2.2 $";
 
 static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 static struct isdn_ppp_compressor *ipc_head = NULL;
@@ -638,23 +646,28 @@
 			break;
 		case PPPIOCGCALLINFO:
 			{
-				struct pppcallinfo pci;
-				memset((char *) &pci,0,sizeof(struct pppcallinfo));
+				struct pppcallinfo *pci;
+
+				if ((pci = (struct pppcallinfo *)kmalloc(sizeof(struct pppcallinfo), GFP_KERNEL)) == NULL)
+					return -ENOMEM;
+				memset((char *) pci,0,sizeof(struct pppcallinfo));
 				if(lp)
 				{
-					strncpy(pci.local_num,lp->msn,63);
+					strncpy(pci->local_num,lp->msn,63);
 					if(lp->dial) {
-						strncpy(pci.remote_num,lp->dial->num,63);
+						strncpy(pci->remote_num,lp->dial->num,63);
 					}
-					pci.charge_units = lp->charge;
+					pci->charge_units = lp->charge;
 					if(lp->outgoing)
-						pci.calltype = CALLTYPE_OUTGOING;
+						pci->calltype = CALLTYPE_OUTGOING;
 					else
-						pci.calltype = CALLTYPE_INCOMING;
+						pci->calltype = CALLTYPE_INCOMING;
 					if(lp->flags & ISDN_NET_CALLBACK)
-						pci.calltype |= CALLTYPE_CALLBACK;
+						pci->calltype |= CALLTYPE_CALLBACK;
 				}
-				return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci);
+				r = set_arg((void *)arg,sizeof(struct pppcallinfo),pci);
+				kfree(pci);
+				return r;
 			}
 		default:
 			break;
@@ -885,7 +898,8 @@
 		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
 			return 0;
 
-		if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
+		if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
+			lp->dialstate == 0 &&
 		    (lp->flags & ISDN_NET_CONNECTED)) {
 			int cnt;
 			struct sk_buff *skb;
@@ -901,6 +915,7 @@
 				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);
@@ -1264,10 +1279,10 @@
 			return;
 	}
 
-	netif_rx(skb);
-	/* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */
 	/* Reset hangup-timer */
 	lp->huptimer = 0;
+	netif_rx(skb);
+	/* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */
 
 	return;
 }
@@ -1376,10 +1391,10 @@
 	 */
 
 	/* 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
-     */
+	 * 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)
@@ -1424,9 +1439,9 @@
 	}
 #endif
 
-    /*
-     * normal or bundle compression
-     */
+	/*
+	 * normal or bundle compression
+	 */
 	skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
 
 	if (ipt->debug & 0x24)
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_syms.c linux/drivers/isdn/isdn_syms.c
--- v2.0.35/linux/drivers/isdn/isdn_syms.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_syms.c	Sun Nov 15 10:33:00 1998
@@ -21,7 +21,7 @@
  * Added GPL-Header, Id and Log
  *
  */
-#include <linux/config.h>
+
 #include <linux/module.h>
 #include <linux/version.h>
 
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.0.35/linux/drivers/isdn/isdn_tty.c	Thu Aug 14 11:19:48 1997
+++ linux/drivers/isdn/isdn_tty.c	Sun Nov 15 10:33:00 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_tty.c,v 1.41 1997/05/27 15:17:31 fritz Exp $
+/* $Id: isdn_tty.c,v 1.41.2.11 1998/11/05 22:12:12 fritz Exp $
 
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,48 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.41.2.11  1998/11/05 22:12:12  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.41.2.10  1998/11/03 14:31:35  fritz
+ * Reduced stack usage in various functions.
+ * Adapted statemachine to work with certified HiSax.
+ * Some fixes in callback handling.
+ *
+ * Revision 1.41.2.9  1998/10/25 15:48:32  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
+ * Revision 1.41.2.8  1998/08/22 16:43:07  armin
+ * Added silence detection in audio receive mode (AT+VSD).
+ *
+ * Revision 1.41.2.7  1998/06/07 13:48:08  fritz
+ * ABC cleanup
+ *
+ * Revision 1.41.2.5  1998/04/08 21:42:35  keil
+ * Blocksize default 1024
+ *
+ * Revision 1.41.2.4  1998/03/19 17:58:55  detabc
+ * remove 2 debug-messages (no longer needed) bug was fixed
+ *
+ * Revision 1.41.2.3  1998/03/07 23:35:20  detabc
+ * added the abc-extension to the linux isdn-kernel
+ * for kernel-version 2.0.xx
+ * DO NOT USE FOR HIGHER KERNELS-VERSIONS
+ * all source-lines are switched with the define  CONFIG_ISDN_WITH_ABC
+ * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes
+ *
+ * you need also a modified isdnctrl-source the switch on the
+ * features of the abc-extension
+ *
+ * please use carefully. more detail will be follow.
+ * thanks
+ *
+ * Revision 1.41.2.2  1998/03/07 23:02:51  tsbogend
+ * fixed kernel unaligned traps on Linux/Alpha
+ *
+ * Revision 1.41.2.1  1997/08/21 15:56:11  fritz
+ * Synchronized 2.0.X branch with 2.0.31-pre7
+ *
  * Revision 1.41  1997/05/27 15:17:31  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -199,6 +241,7 @@
 #define VBUFX (VBUF/16)
 #endif
 
+
 /* Prototypes */
 
 static int isdn_tty_edit_at(const char *, int, modem_info *, int);
@@ -223,7 +266,7 @@
 static int si2bit[8] =
 {4, 1, 4, 4, 4, 4, 4, 4};
 
-char *isdn_tty_revision = "$Revision: 1.41 $";
+char *isdn_tty_revision = "$Revision: 1.41.2.11 $";
 
 #define DLE 0x10
 #define ETX 0x03
@@ -308,6 +351,8 @@
 				r = 0;
 #ifdef CONFIG_ISDN_AUDIO
 				isdn_audio_eval_dtmf(info);
+        			if ((info->vonline & 1) && (info->emu.vpar[1]))
+                                        isdn_audio_eval_silence(info);
 #endif
 				if ((tty = info->tty)) {
 					if (info->mcr & UART_MCR_RTS) {
@@ -364,6 +409,8 @@
 	
 	if (info->vonline)
 		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+        if ((info->vonline & 1) && (info->emu.vpar[1]))
+                isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
 #endif
 	if ((info->online < 2)
 #ifdef CONFIG_ISDN_AUDIO
@@ -798,20 +845,20 @@
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
 		cmd.command = ISDN_CMD_CLREAZ;
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_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);
+		isdn_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);
+		isdn_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);
+		isdn_command(&cmd);
 		cmd.driver = info->isdn_driver;
 		cmd.arg = info->isdn_channel;
 		sprintf(cmd.parm.setup.phone, "%s", n);
@@ -823,7 +870,7 @@
 		info->dialing = 1;
 		strcpy(dev->num[i], n);
 		isdn_info_update();
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_command(&cmd);
 	}
 }
 
@@ -872,7 +919,7 @@
 			cmd.driver = info->isdn_driver;
 			cmd.command = ISDN_CMD_HANGUP;
 			cmd.arg = info->isdn_channel;
-			dev->drv[info->isdn_driver]->interface->command(&cmd);
+			isdn_command(&cmd);
 		}
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
 		info->emu.mdmreg[1] = 0;
@@ -1270,7 +1317,7 @@
 	status = info->lsr;
 	restore_flags(flags);
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-	put_user(result, (ulong *) value);
+	put_user(result, (uint *) value);
 	return 0;
 }
 
@@ -1294,7 +1341,7 @@
 	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
 	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
 	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-	put_user(result, (ulong *) value);
+	put_user(result, (uint *) value);
 	return 0;
 }
 
@@ -1443,7 +1490,6 @@
 				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);
@@ -1927,7 +1973,7 @@
 #ifdef CONFIG_ISDN_AUDIO
 		skb_queue_head_init(&info->dtmf_queue);
 #endif
-		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
 			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
 			return -3;
 		}
@@ -1944,9 +1990,11 @@
  * Return Index to dev->mdm or -1 if none found.
  */
 int
-isdn_tty_find_icall(int di, int ch, setup_parm setup)
+isdn_tty_find_icall(int di, isdn_ctrl *c)
 {
 	char *eaz;
+	int ch = c->arg;
+	setup_parm *setup = &c->parm.setup;
 	int i;
 	int idx;
 	int si1;
@@ -1956,19 +2004,19 @@
 
 	save_flags(flags);
 	cli();
-	if (!setup.phone[0]) {
+	if (!setup->phone[0]) {
 		nr[0] = '0';
 		nr[1] = '\0';
 		printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
 	} else
-		strcpy(nr, setup.phone);
-	si1 = (int) setup.si1;
-	si2 = (int) setup.si2;
-	if (!setup.eazmsn[0]) {
+		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;
+		eaz = setup->eazmsn;
 #ifdef ISDN_DEBUG_MODEM_ICALL
 	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
 #endif
@@ -2002,8 +2050,8 @@
 				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[21] = setup.plan;
-				info->emu.mdmreg[22] = setup.screen;
+				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,
@@ -2016,7 +2064,7 @@
 		}
 	}
 	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
-	       dev->drv[di]->reject_bus ? "rejected" : "ignored");
+	       (dev->drv[di]->flags & DRV_FLAG_REJBUS) ? "rejected" : "ignored");
 	restore_flags(flags);
 	return -1;
 }
@@ -2534,7 +2582,7 @@
 			/* &B - Set Buffersize */
 			p[0]++;
 			i = isdn_getnum(p);
-			if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
+			if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
 				PARSE_ERROR1;
 #ifdef CONFIG_ISDN_AUDIO
 			if ((m->mdmreg[18] & 1) && (i > VBUF))
@@ -2643,7 +2691,7 @@
 				return 1;
 			break;
 		case 16:
-			if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
+			if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
 				return 1;
 #ifdef CONFIG_ISDN_AUDIO
 			if ((m->mdmreg[18] & 1) && (mval > VBUFX))
@@ -2756,15 +2804,15 @@
 		cmd.command = ISDN_CMD_SETL2;
 		cmd.arg = info->isdn_channel + (l2 << 8);
 		info->last_l2 = l2;
-		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		isdn_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);
+		isdn_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);
+		isdn_command(&cmd);
 	} else
 		isdn_tty_modem_result(8, info);
 }
@@ -2937,6 +2985,11 @@
 				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
 				PARSE_ERROR1;
 			}
+                        info->silence_state = isdn_audio_silence_init(info->silence_state);
+                        if (!info->silence_state) {
+                                printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
+                                PARSE_ERROR1;
+                        }
 			if (m->vpar[3] < 5) {
 				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
 				if (!info->adpcmr) {
@@ -2962,33 +3015,29 @@
 					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;
+                                        p[0]++;
+                                        if ((*p[0]>='0') && (*p[0]<='9')) {
+                                                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;
+                                        } else
+                                        if (*p[0] == '?') {
+                                                p[0]++;
+                                                isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+                                                           info);
+                                                break;
+                                        } else
+                                        PARSE_ERROR1;
+                                        break;
 				default:
 					PARSE_ERROR1;
 			}
@@ -3095,7 +3144,7 @@
 				break;
 			case 'D':
 				/* D - Dial */
-				isdn_tty_getdial(++p, ds, sizeof(ds));
+				isdn_tty_getdial(++p, ds,sizeof(ds));
 				p += strlen(p);
 				if (!strlen(m->msn))
 					isdn_tty_modem_result(10, info);
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h
--- v2.0.35/linux/drivers/isdn/isdn_tty.h	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/isdn_tty.h	Sun Nov 15 10:33:00 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn_tty.h,v 1.10 1997/03/02 14:29:26 fritz Exp $
+/* $Id: isdn_tty.h,v 1.10.2.2 1998/11/05 22:12:25 fritz Exp $
 
  * header for Linux ISDN subsystem, tty related functions (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.h,v $
+ * Revision 1.10.2.2  1998/11/05 22:12:25  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.10.2.1  1998/10/25 15:48:44  fritz
+ * Misc bugfixes and adaptions to new HiSax
+ *
  * Revision 1.10  1997/03/02 14:29:26  fritz
  * More ttyI related cleanup.
  *
@@ -59,7 +65,7 @@
 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 int isdn_tty_find_icall(int, isdn_ctrl *);
 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.35/linux/drivers/isdn/isdnloop/Makefile linux/drivers/isdn/isdnloop/Makefile
--- v2.0.35/linux/drivers/isdn/isdnloop/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdnloop/Makefile	Sun Nov 15 10:33:01 1998
@@ -0,0 +1,11 @@
+L_OBJS :=
+M_OBJS :=
+
+ifeq ($(CONFIG_ISDN_DRV_LOOP),y)
+  L_OBJS += isdnloop.o
+else
+  M_OBJS += isdnloop.o
+endif
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.c linux/drivers/isdn/isdnloop/isdnloop.c
--- v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdnloop/isdnloop.c	Sun Nov 15 10:33:01 1998
@@ -0,0 +1,1563 @@
+/* $Id: isdnloop.c,v 1.1.2.1 1998/11/05 22:13:15 fritz Exp $
+
+ * ISDN low-level module implementing a dummy loop driver.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.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)
+ * 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: isdnloop.c,v $
+ * Revision 1.1.2.1  1998/11/05 22:13:15  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.1  1997/03/24 23:02:04  fritz
+ * Added isdnloop driver.
+ *
+ */
+
+#include "isdnloop.h"
+
+static char
+*revision = "$Revision: 1.1.2.1 $";
+
+static int isdnloop_addcard(char *);
+
+/*
+ * Free queue completely.
+ *
+ * Parameter:
+ *   card    = pointer to card struct
+ *   channel = channel number
+ */
+static void
+isdnloop_free_queue(isdnloop_card * card, int channel)
+{
+	struct sk_buff_head *queue = &card->bqueue[channel];
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(queue)))
+		dev_kfree_skb(skb, FREE_WRITE);
+	card->sndcount[channel] = 0;
+}
+
+/*
+ * Send B-Channel data to another virtual card.
+ * This routine is called via timer-callback from isdnloop_pollbchan().
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel number (0-based)
+ */
+static void
+isdnloop_bchan_send(isdnloop_card * card, int ch)
+{
+	isdnloop_card *rcard = card->rcard[ch];
+	int rch = card->rch[ch];
+	struct sk_buff *skb;
+	isdn_ctrl cmd;
+
+	while (card->sndcount[ch]) {
+		if ((skb = skb_dequeue(&card->bqueue[ch]))) {
+			card->sndcount[ch] -= skb->len;
+			if (rcard)
+				rcard->interface.rcvcallb_skb(rcard->myid, rch, skb);
+			cmd.command = ISDN_STAT_BSENT;
+			cmd.driver = card->myid;
+			cmd.arg = ch;
+			card->interface.statcallb(&cmd);
+		} else
+			card->sndcount[ch] = 0;
+	}
+}
+
+/*
+ * Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ *
+ * Parameter:
+ *   data = pointer to card struct, set by kernel timer.data
+ */
+static void
+isdnloop_pollbchan(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	unsigned long flags;
+
+	if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE)
+		isdnloop_bchan_send(card, 0);
+	if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE)
+		isdnloop_bchan_send(card, 1);
+	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) {
+		/* schedule b-channel polling again */
+		save_flags(flags);
+		cli();
+		card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+		add_timer(&card->rb_timer);
+		card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+		restore_flags(flags);
+	} else
+		card->flags &= ~ISDNLOOP_FLAGS_RBTIMER;
+}
+
+/*
+ * Parse ICN-type setup string and fill fields of setup-struct
+ * with parsed data.
+ *
+ * Parameter:
+ *   setup = setup string, format: [caller-id],si1,si2,[called-id]
+ *   cmd   = pointer to struct to be filled.
+ */
+static void
+isdnloop_parse_setup(char *setup, isdn_ctrl * cmd)
+{
+	char *t = setup;
+	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;
+}
+
+typedef struct isdnloop_stat {
+	char *statstr;
+	int command;
+	int action;
+} isdnloop_stat;
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_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* */
+
+
+/*
+ * Parse Status message-strings from virtual card.
+ * Depending on status, call statcallb for sending messages to upper
+ * levels. Also set/reset B-Channel active-flags.
+ *
+ * Parameter:
+ *   status  = status string to parse.
+ *   channel = channel where message comes from.
+ *   card    = card where message comes from.
+ */
+static void
+isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card)
+{
+	isdnloop_stat *s = isdnloop_stat_table;
+	int action = -1;
+	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;
+	cmd.driver = card->myid;
+	cmd.arg = channel;
+	switch (action) {
+		case 1:
+			/* BCON_x */
+			card->flags |= (channel) ?
+			    ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE;
+			break;
+		case 2:
+			/* BDIS_x */
+			card->flags &= ~((channel) ?
+					 ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE);
+			isdnloop_free_queue(card, channel);
+			break;
+		case 3:
+			/* DCAL_I and DSCA_I */
+			isdnloop_parse_setup(status + 6, &cmd);
+			break;
+		case 4:
+			/* FCALL */
+			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:
+			/* CIF */
+			strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1);
+			break;
+		case 6:
+			/* AOC */
+			sprintf(cmd.parm.num, "%d",
+			     (int) simple_strtoul(status + 7, NULL, 16));
+			break;
+		case 7:
+			/* CAU */
+			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:
+			/* Misc Errors on L1 and L2 */
+			card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE;
+			isdnloop_free_queue(card, 0);
+			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 &= ~ISDNLOOP_FLAGS_B2ACTIVE;
+			isdnloop_free_queue(card, 1);
+			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);
+}
+
+/*
+ * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   c    = char to store.
+ */
+static void
+isdnloop_putmsg(isdnloop_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);
+}
+
+/*
+ * Poll a virtual cards message queue.
+ * If there are new status-replies from the card, copy them to
+ * ringbuffer for reading on /dev/isdnctrl and call
+ * isdnloop_parse_status() for processing them. Watch for special
+ * Firmware bootmessage and parse it, to get the D-Channel protocol.
+ * If there are B-Channels open, initiate a timer-callback to
+ * isdnloop_pollbchan().
+ * This routine is called periodically via timer interrupt.
+ *
+ * Parameter:
+ *   data = pointer to card struct
+ */
+static void
+isdnloop_polldchan(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	struct sk_buff *skb;
+	int avail;
+	int left;
+	u_char c;
+	int ch;
+	int flags;
+	u_char *p;
+	isdn_ctrl cmd;
+
+	if ((skb = skb_dequeue(&card->dqueue)))
+		avail = skb->len;
+	else
+		avail = 0;
+	for (left = avail; left > 0; left--) {
+		c = *skb->data;
+		skb_pull(skb, 1);
+		isdnloop_putmsg(card, c);
+		card->imsg[card->iptr] = c;
+		if (card->iptr < 59)
+			card->iptr++;
+		if (!skb->len) {
+			avail++;
+			isdnloop_putmsg(card, '\n');
+			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];
+				isdnloop_parse_status(p, ch, card);
+			} else {
+				p = card->imsg;
+				if (!strncmp(p, "DRV1.", 5)) {
+					printk(KERN_INFO "isdnloop: (%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
+						       "isdnloop: (%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
+						       "isdnloop: (%s) Euro-Protocol loaded and running\n", CID);
+					}
+					continue;
+
+				}
+			}
+		}
+	}
+	if (avail) {
+		cmd.command = ISDN_STAT_STAVAIL;
+		cmd.driver = card->myid;
+		cmd.arg = avail;
+		card->interface.statcallb(&cmd);
+	}
+	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE))
+		if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) {
+			/* schedule b-channel polling */
+			card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+			save_flags(flags);
+			cli();
+			del_timer(&card->rb_timer);
+			card->rb_timer.function = isdnloop_pollbchan;
+			card->rb_timer.data = (unsigned long) card;
+			card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+			add_timer(&card->rb_timer);
+			restore_flags(flags);
+		}
+	/* schedule again */
+	save_flags(flags);
+	cli();
+	card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+	add_timer(&card->st_timer);
+	restore_flags(flags);
+}
+
+/*
+ * Append a packet to the transmit buffer-queue.
+ *
+ * Parameter:
+ *   channel = Number of B-channel
+ *   skb     = packet to send.
+ *   card    = pointer to card-struct
+ * Return:
+ *   Number of bytes transferred, -E??? on error
+ */
+static int
+isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card)
+{
+	int len = skb->len;
+	unsigned long flags;
+	struct sk_buff *nskb;
+
+	if (len > 4000) {
+		printk(KERN_WARNING
+		       "isdnloop: Send packet too large\n");
+		return -EINVAL;
+	}
+	if (len) {
+		if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE))
+			return 0;
+		if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE)
+			return 0;
+		save_flags(flags);
+		cli();
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			skb_queue_tail(&card->bqueue[channel], nskb);
+			dev_kfree_skb(skb, FREE_WRITE);
+		} else
+			len = 0;
+		card->sndcount[channel] += len;
+		restore_flags(flags);
+	}
+	return len;
+}
+
+/*
+ * Read the messages from the card's ringbuffer
+ *
+ * Parameter:
+ *   buf  = pointer to buffer.
+ *   len  = number of bytes to read.
+ *   user = flag, 1: called from userlevel 0: called from kernel.
+ *   card = pointer to card struct.
+ * Return:
+ *   number of bytes actually transferred.
+ */
+static int
+isdnloop_readstatus(u_char * buf, int len, int user, isdnloop_card * card)
+{
+	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;
+}
+
+/*
+ * Simulate a card's response by appending it to the cards
+ * message queue.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   s    = pointer to message-string.
+ *   ch   = channel: 0 = generic messages, 1 and 2 = D-channel messages.
+ * Return:
+ *   0 on success, 1 on memory squeeze.
+ */
+static int
+isdnloop_fake(isdnloop_card * card, char *s, int ch)
+{
+	struct sk_buff *skb;
+	int len = strlen(s) + ((ch >= 0) ? 3 : 0);
+
+	if (!(skb = dev_alloc_skb(len))) {
+		printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n");
+		return 1;
+	}
+	if (ch >= 0)
+		sprintf(skb_put(skb, 3), "%02d;", ch);
+	memcpy(skb_put(skb, strlen(s)), s, strlen(s));
+	skb_queue_tail(&card->dqueue, skb);
+	return 0;
+}
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_cmd_table[] =
+{
+	{"BCON_R",         0,  1},	/* B-Channel connect        */
+	{"BDIS_R",         0,  2},	/* B-Channel disconnect     */
+	{"DDIS_R",         0,  3},	/* D-Channel disconnect     */
+	{"DCON_R",         0, 16},	/* D-Channel connect        */
+	{"DSCA_R",         0,  4},	/* Dial 1TR6-SPV     */
+	{"DCAL_R",         0,  5},	/* Dial */
+	{"EAZC",           0,  6},	/* Clear EAZ listener */
+	{"EAZ",            0,  7},	/* Set EAZ listener */
+	{"SEEAZ",          0,  8},	/* Get EAZ listener */
+	{"MSN",            0,  9},	/* Set/Clear MSN listener */
+	{"MSALL",          0, 10},	/* Set multi MSN listeners */
+	{"SETSIL",         0, 11},	/* Set SI list     */
+	{"SEESIL",         0, 12},	/* Get SI list     */
+	{"SILC",           0, 13},	/* Clear SI list     */
+	{"LOCK",           0, -1},	/* LOCK channel     */
+	{"UNLOCK",         0, -1},	/* UNLOCK channel     */
+	{"FV2ON",          1, 14},	/* Leased mode on               */
+	{"FV2OFF",         1, 15},	/* Leased mode off              */
+	{NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Simulate an error-response from a card.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ */
+static void
+isdnloop_fake_err(isdnloop_card * card)
+{
+	char buf[60];
+
+	sprintf(buf, "E%s", card->omsg);
+	isdnloop_fake(card, buf, -1);
+	isdnloop_fake(card, "NAK", -1);
+}
+
+static u_char ctable_eu[] =
+{0x00, 0x11, 0x01, 0x12};
+static u_char ctable_1t[] =
+{0x00, 0x3b, 0x01, 0x3a};
+
+/*
+ * Assemble a simplified cause message depending on the
+ * D-channel protocol used.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   loc  = location: 0 = local, 1 = remote.
+ *   cau  = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding.
+ * Return:
+ *   Pointer to buffer containing the assembled message.
+ */
+static char *
+isdnloop_unicause(isdnloop_card * card, int loc, int cau)
+{
+	static char buf[6];
+
+	switch (card->ptype) {
+		case ISDN_PTYPE_EURO:
+			sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]);
+			break;
+		case ISDN_PTYPE_1TR6:
+			sprintf(buf, "%02X44", ctable_1t[cau]);
+			break;
+		default:
+			return ("0000");
+	}
+	return (buf);
+}
+
+/*
+ * Release a virtual connection. Called from timer interrupt, when
+ * called party did not respond.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel (0-based)
+ */
+static void
+isdnloop_atimeout(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+	char buf[60];
+
+	save_flags(flags);
+	cli();
+	if (card->rcard) {
+		isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1);
+		card->rcard[ch]->rcard[card->rch[ch]] = NULL;
+		card->rcard[ch] = NULL;
+	}
+	isdnloop_fake(card, "DDIS_I", ch + 1);
+	/* No user responding */
+	sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3));
+	isdnloop_fake(card, buf, ch + 1);
+	restore_flags(flags);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout0(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	isdnloop_atimeout(card, 0);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout1(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	isdnloop_atimeout(card, 1);
+}
+
+/*
+ * Install a watchdog for a user, not responding.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel to watch for.
+ */
+static void
+isdnloop_start_ctimer(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	init_timer(&card->c_timer[ch]);
+	card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT;
+	if (ch)
+		card->c_timer[ch].function = isdnloop_atimeout1;
+	else
+		card->c_timer[ch].function = isdnloop_atimeout0;
+	card->c_timer[ch].data = (unsigned long) card;
+	add_timer(&card->c_timer[ch]);
+	restore_flags(flags);
+}
+
+/*
+ * Kill a pending channel watchdog.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel (0-based).
+ */
+static void
+isdnloop_kill_ctimer(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	del_timer(&card->c_timer[ch]);
+	restore_flags(flags);
+}
+
+static u_char si2bit[] =
+{0, 1, 0, 0, 0, 2, 0, 4, 0, 0};
+static u_char bit2si[] =
+{1, 5, 7};
+
+/*
+ * Try finding a listener for an outgoing call.
+ *
+ * Parameter:
+ *   card = pointer to calling card.
+ *   p    = pointer to ICN-type setup-string.
+ *   lch  = channel of calling card.
+ *   cmd  = pointer to struct to be filled when parsing setup.
+ * Return:
+ *   0 = found match, alerting should happen.
+ *   1 = found matching number but it is busy.
+ *   2 = no matching listener.
+ *   3 = found matching number but SI does not match.
+ */
+static int
+isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd)
+{
+	isdnloop_card *cc = cards;
+	unsigned long flags;
+	int ch;
+	int num_match;
+	int i;
+	char *e;
+	char nbuf[32];
+
+	isdnloop_parse_setup(p, cmd);
+	while (cc) {
+		for (ch = 0; ch < 2; ch++) {
+			/* Exclude ourself */
+			if ((cc == card) && (ch == lch))
+				continue;
+			num_match = 0;
+			switch (cc->ptype) {
+				case ISDN_PTYPE_EURO:
+					for (i = 0; i < 3; i++)
+						if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone)))
+							num_match = 1;
+					break;
+				case ISDN_PTYPE_1TR6:
+					e = cc->eazlist[ch];
+					while (*e) {
+						sprintf(nbuf, "%s%c", cc->s0num[0], *e);
+						if (!(strcmp(nbuf, cmd->parm.setup.phone)))
+							num_match = 1;
+						e++;
+					}
+			}
+			if (num_match) {
+				save_flags(flags);
+				cli();
+				/* channel idle? */
+				if (!(cc->rcard[ch])) {
+					/* Check SI */
+					if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) {
+						restore_flags(flags);
+						return 3;
+					}
+					/* ch is idle, si and number matches */
+					cc->rcard[ch] = card;
+					cc->rch[ch] = lch;
+					card->rcard[lch] = cc;
+					card->rch[lch] = ch;
+					restore_flags(flags);
+					return 0;
+				} else {
+					restore_flags(flags);
+					/* num matches, but busy */
+					if (ch == 1)
+						return 1;
+				}
+			}
+		}
+		cc = cc->next;
+	}
+	return 2;
+}
+
+/*
+ * Depending on D-channel protocol and caller/called, modify
+ * phone number.
+ *
+ * Parameter:
+ *   card   = pointer to card struct.
+ *   phone  = pointer phone number.
+ *   caller = flag: 1 = caller, 0 = called.
+ * Return:
+ *   pointer to new phone number.
+ */
+static char *
+isdnloop_vstphone(isdnloop_card * card, char *phone, int caller)
+{
+	int i;
+	static char nphone[30];
+
+	switch (card->ptype) {
+		case ISDN_PTYPE_EURO:
+			if (caller) {
+				for (i = 0; i < 2; i++)
+					if (!(strcmp(card->s0num[i], phone)))
+						return (phone);
+				return (card->s0num[0]);
+			}
+			return (phone);
+			break;
+		case ISDN_PTYPE_1TR6:
+			if (caller) {
+				sprintf(nphone, "%s%c", card->s0num[0], phone[0]);
+				return (nphone);
+			} else
+				return (&phone[strlen(phone) - 1]);
+			break;
+	}
+	return ("\0");
+}
+
+/*
+ * Parse an ICN-type command string sent to the 'card'.
+ * Perform misc. actions depending on the command.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ */
+static void
+isdnloop_parse_cmd(isdnloop_card * card)
+{
+	char *p = card->omsg;
+	isdn_ctrl cmd;
+	char buf[60];
+	isdnloop_stat *s = isdnloop_cmd_table;
+	int action = -1;
+	int i;
+	int ch;
+
+	if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) {
+		isdnloop_fake_err(card);
+		return;
+	}
+	ch = card->omsg[1] - '0';
+	if ((ch < 0) || (ch > 2)) {
+		isdnloop_fake_err(card);
+		return;
+	}
+	p += 3;
+	while (s->statstr) {
+		if (!strncmp(p, s->statstr, strlen(s->statstr))) {
+			action = s->action;
+			if (s->command && (ch != 0)) {
+				isdnloop_fake_err(card);
+				return;
+			}
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return;
+	switch (action) {
+		case 1:
+			/* 0x;BCON_R */
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card, "BCON_C", ch);
+				isdnloop_fake(card->rcard[ch - 1], "BCON_I",
+					      card->rch[ch - 1] + 1);
+			}
+			break;
+		case 2:
+			/* 0x;BDIS_R */
+			isdnloop_fake(card, "BDIS_C", ch);
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card->rcard[ch - 1], "BDIS_I",
+					      card->rch[ch - 1] + 1);
+			}
+			break;
+		case 16:
+			/* 0x;DCON_R */
+			isdnloop_kill_ctimer(card, ch - 1);
+			if (card->rcard[ch - 1]) {
+				isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+				isdnloop_fake(card->rcard[ch - 1], "DCON_C",
+					      card->rch[ch - 1] + 1);
+				isdnloop_fake(card, "DCON_C", ch);
+			}
+			break;
+		case 3:
+			/* 0x;DDIS_R */
+			isdnloop_kill_ctimer(card, ch - 1);
+			if (card->rcard[ch - 1]) {
+				isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+				isdnloop_fake(card->rcard[ch - 1], "DDIS_I",
+					      card->rch[ch - 1] + 1);
+				card->rcard[ch - 1] = NULL;
+			}
+			isdnloop_fake(card, "DDIS_C", ch);
+			break;
+		case 4:
+			/* 0x;DSCA_Rdd,yy,zz,oo */
+			if (card->ptype != ISDN_PTYPE_1TR6) {
+				isdnloop_fake_err(card);
+				return;
+			}
+			/* Fall through */
+		case 5:
+			/* 0x;DCAL_Rdd,yy,zz,oo */
+			p += 6;
+			switch (isdnloop_try_call(card, p, ch - 1, &cmd)) {
+				case 0:
+					/* Alerting */
+					sprintf(buf, "D%s_I%s,%02d,%02d,%s",
+					   (action == 4) ? "SCA" : "CAL",
+						isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1),
+						cmd.parm.setup.si1,
+						cmd.parm.setup.si2,
+					isdnloop_vstphone(card->rcard[ch],
+					       cmd.parm.setup.phone, 0));
+					isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1);
+					/* Fall through */
+				case 3:
+					/* si1 does not match, dont alert but start timer */
+					isdnloop_start_ctimer(card, ch - 1);
+					break;
+				case 1:
+					/* Remote busy */
+					isdnloop_fake(card, "DDIS_I", ch);
+					sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1));
+					isdnloop_fake(card, buf, ch);
+					break;
+				case 2:
+					/* No such user */
+					isdnloop_fake(card, "DDIS_I", ch);
+					sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2));
+					isdnloop_fake(card, buf, ch);
+					break;
+			}
+			break;
+		case 6:
+			/* 0x;EAZC */
+			card->eazlist[ch - 1][0] = '\0';
+			break;
+		case 7:
+			/* 0x;EAZ */
+			p += 3;
+			strcpy(card->eazlist[ch - 1], p);
+			break;
+		case 8:
+			/* 0x;SEEAZ */
+			sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]);
+			isdnloop_fake(card, buf, ch + 1);
+			break;
+		case 9:
+			/* 0x;MSN */
+			break;
+		case 10:
+			/* 0x;MSNALL */
+			break;
+		case 11:
+			/* 0x;SETSIL */
+			p += 6;
+			i = 0;
+			while (strchr("0157", *p)) {
+				if (i)
+					card->sil[ch - 1] |= si2bit[*p - '0'];
+				i = (*p++ == '0');
+			}
+			if (*p)
+				isdnloop_fake_err(card);
+			break;
+		case 12:
+			/* 0x;SEESIL */
+			sprintf(buf, "SIN-LIST: ");
+			p = buf + 10;
+			for (i = 0; i < 3; i++)
+				if (card->sil[ch - 1] & (1 << i))
+					p += sprintf(p, "%02d", bit2si[i]);
+			isdnloop_fake(card, buf, ch + 1);
+			break;
+		case 13:
+			/* 0x;SILC */
+			card->sil[ch - 1] = 0;
+			break;
+		case 14:
+			/* 00;FV2ON */
+			break;
+		case 15:
+			/* 00;FV2OFF */
+			break;
+	}
+}
+
+/*
+ * Put command-strings into the of the 'card'. In reality, execute them
+ * right in place by calling isdnloop_parse_cmd(). Also copy every
+ * command to the read message ringbuffer, preceeding it with a '>'.
+ * These mesagges can be read at /dev/isdnctrl.
+ *
+ * Parameter:
+ *   buf  = pointer to command buffer.
+ *   len  = length of buffer data.
+ *   user = flag: 1 = called form userlevel, 0 called from kernel.
+ *   card = pointer to card struct.
+ * Return:
+ *   number of bytes transfered (currently always equals len).
+ */
+static int
+isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card)
+{
+	int xcount = 0;
+	int ocount = 1;
+	isdn_ctrl cmd;
+
+	while (len) {
+		int count = MIN(255, len);
+		u_char *p;
+		u_char msg[0x100];
+
+		if (user)
+			copy_from_user(msg, buf, count);
+		else
+			memcpy(msg, buf, count);
+		isdnloop_putmsg(card, '>');
+		for (p = msg; count > 0; count--, p++) {
+			len--;
+			xcount++;
+			isdnloop_putmsg(card, *p);
+			card->omsg[card->optr] = *p;
+			if (*p == '\n') {
+				card->omsg[card->optr] = '\0';
+				card->optr = 0;
+				isdnloop_parse_cmd(card);
+				if (len) {
+					isdnloop_putmsg(card, '>');
+					ocount++;
+				}
+			} else {
+				if (card->optr < 59)
+					card->optr++;
+			}
+			ocount++;
+		}
+	}
+	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
+isdnloop_stopcard(isdnloop_card * card)
+{
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	save_flags(flags);
+	cli();
+	if (card->flags & ISDNLOOP_FLAGS_RUNNING) {
+		card->flags &= ~ISDNLOOP_FLAGS_RUNNING;
+		del_timer(&card->st_timer);
+		del_timer(&card->rb_timer);
+		del_timer(&card->c_timer[0]);
+		del_timer(&card->c_timer[1]);
+		cmd.command = ISDN_STAT_STOP;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+	}
+	restore_flags(flags);
+}
+
+/*
+ * Stop all cards before unload.
+ */
+static void
+isdnloop_stopallcards(void)
+{
+	isdnloop_card *p = cards;
+
+	while (p) {
+		isdnloop_stopcard(p);
+		p = p->next;
+	}
+}
+
+/*
+ * Start a 'card'. Simulate card's boot message and set the phone
+ * number(s) of the virtual 'S0-Interface'. Install D-channel
+ * poll timer.
+ *
+ * Parameter:
+ *   card  = pointer to card struct.
+ *   sdefp = pointer to struct holding ioctl parameters.
+ * Return:
+ *   0 on success, -E??? otherwise.
+ */
+static int
+isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp)
+{
+	unsigned long flags;
+	isdnloop_sdef sdef;
+	int i;
+
+	if (card->flags & ISDNLOOP_FLAGS_RUNNING)
+		return -EBUSY;
+	copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef));
+	save_flags(flags);
+	cli();
+	switch (sdef.ptype) {
+		case ISDN_PTYPE_EURO:
+			if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96",
+					  -1)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			card->sil[0] = card->sil[1] = 4;
+			if (isdnloop_fake(card, "TEI OK", 0)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			for (i = 0; i < 3; i++)
+				strcpy(card->s0num[i], sdef.num[i]);
+			break;
+		case ISDN_PTYPE_1TR6:
+			if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95",
+					  -1)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			card->sil[0] = card->sil[1] = 4;
+			if (isdnloop_fake(card, "TEI OK", 0)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			strcpy(card->s0num[0], sdef.num[0]);
+			card->s0num[1][0] = '\0';
+			card->s0num[2][0] = '\0';
+			break;
+		default:
+			restore_flags(flags);
+			printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n",
+			       sdef.ptype);
+			return -EINVAL;
+	}
+	init_timer(&card->st_timer);
+	card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+	card->st_timer.function = isdnloop_polldchan;
+	card->st_timer.data = (unsigned long) card;
+	add_timer(&card->st_timer);
+	card->flags |= ISDNLOOP_FLAGS_RUNNING;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Main handler for commands sent by linklevel.
+ */
+static int
+isdnloop_command(isdn_ctrl * c, isdnloop_card * card)
+{
+	ulong a;
+	int i;
+	char cbuf[60];
+	isdn_ctrl cmd;
+	isdnloop_cdef cdef;
+
+	switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			switch (c->arg) {
+				case ISDNLOOP_IOCTL_DEBUGVAR:
+					return (ulong) card;
+				case ISDNLOOP_IOCTL_STARTUP:
+					if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef))))
+						return i;
+					return (isdnloop_start(card, (isdnloop_sdef *) a));
+					break;
+				case ISDNLOOP_IOCTL_ADDCARD:
+					if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(isdnloop_cdef))))
+						return i;
+					copy_from_user((char *) &cdef, (char *) a, sizeof(cdef));
+					return (isdnloop_addcard(cdef.id1));
+					break;
+				case ISDNLOOP_IOCTL_LEASEDCFG:
+					if (a) {
+						if (!card->leased) {
+							card->leased = 1;
+							while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+								current->timeout = jiffies + 10;
+								schedule();
+							}
+							current->timeout = jiffies + 10;
+							schedule();
+							sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n");
+							i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "isdnloop: (%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 = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "isdnloop: (%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 & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if ((c->arg & 255) < ISDNLOOP_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 = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ISDNLOOP_BCH) {
+				a = c->arg + 1;
+				cbuf[0] = 0;
+				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;
+				}
+				if (strlen(cbuf))
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ISDNLOOP_BCH) {
+				a = c->arg + 1;
+				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;
+					default:
+						sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+				}
+				printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				break;
+		case ISDN_CMD_HANGUP:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (c->arg < ISDNLOOP_BCH) {
+					a = c->arg + 1;
+					sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_SETEAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (card->leased)
+					break;
+				if (c->arg < ISDNLOOP_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 = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_CLREAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (card->leased)
+					break;
+				if (c->arg < ISDNLOOP_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 = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_SETL2:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if ((c->arg & 255) < ISDNLOOP_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 = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+					card->l2_proto[a & 255] = (a >> 8);
+				}
+				break;
+		case ISDN_CMD_GETL2:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if ((c->arg & 255) < ISDNLOOP_BCH)
+					return card->l2_proto[c->arg & 255];
+				else
+					return -ENODEV;
+		case ISDN_CMD_SETL3:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				return 0;
+		case ISDN_CMD_GETL3:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if ((c->arg & 255) < ISDNLOOP_BCH)
+					return ISDN_PROTO_L3_TRANS;
+				else
+					return -ENODEV;
+		case ISDN_CMD_GETEAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				break;
+		case ISDN_CMD_SETSIL:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				break;
+		case ISDN_CMD_GETSIL:
+				if (!card->flags & ISDNLOOP_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;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline isdnloop_card *
+isdnloop_findcard(int driverid)
+{
+	isdnloop_card *p = cards;
+
+	while (p) {
+		if (p->myid == driverid)
+			return p;
+		p = p->next;
+	}
+	return (isdnloop_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+	isdnloop_card *card = isdnloop_findcard(c->driver);
+
+	if (card)
+		return (isdnloop_command(c, card));
+	printk(KERN_ERR
+	       "isdnloop: if_command called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		return (isdnloop_writecmd(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_writecmd called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		return (isdnloop_readstatus(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_readstatus called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, struct sk_buff *skb)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		return (isdnloop_sendbuf(channel, skb, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_readstatus 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 isdnloop_card *
+isdnloop_initcard(char *id)
+{
+	isdnloop_card *card;
+	int i;
+
+	if (!(card = (isdnloop_card *) kmalloc(sizeof(isdnloop_card), GFP_KERNEL))) {
+		printk(KERN_WARNING
+		 "isdnloop: (%s) Could not allocate card-struct.\n", id);
+		return (isdnloop_card *) 0;
+	}
+	memset((char *) card, 0, sizeof(isdnloop_card));
+	card->interface.channels = ISDNLOOP_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 < ISDNLOOP_BCH; i++) {
+		card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+		skb_queue_head_init(&card->bqueue[i]);
+	}
+	skb_queue_head_init(&card->dqueue);
+	card->next = cards;
+	cards = card;
+	if (!register_isdn(&card->interface)) {
+		cards = cards->next;
+		printk(KERN_WARNING
+		       "isdnloop: Unable to register %s\n", id);
+		kfree(card);
+		return (isdnloop_card *) 0;
+	}
+	card->myid = card->interface.channels;
+	return card;
+}
+
+static int
+isdnloop_addcard(char *id1)
+{
+	ulong flags;
+	isdnloop_card *card;
+
+	save_flags(flags);
+	cli();
+	if (!(card = isdnloop_initcard(id1))) {
+		restore_flags(flags);
+		return -EIO;
+	}
+	restore_flags(flags);
+	printk(KERN_INFO
+	       "isdnloop: (%s) virtual card added\n",
+	       card->interface.id);
+	return 0;
+}
+
+#ifdef MODULE
+#define isdnloop_init init_module
+#else
+void
+isdnloop_setup(char *str, int *ints)
+{
+	static char sid[20];
+
+	if (strlen(str)) {
+		strcpy(sid, str);
+		isdnloop_id = sid;
+	}
+}
+#endif
+
+int
+isdnloop_init(void)
+{
+	char *p;
+	char rev[10];
+
+	/* 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 "isdnloop-ISDN-driver Rev%s\n", rev);
+	return (isdnloop_addcard(isdnloop_id));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+	isdn_ctrl cmd;
+	isdnloop_card *card = cards;
+	isdnloop_card *last;
+	int i;
+
+	isdnloop_stopallcards();
+	while (card) {
+		cmd.command = ISDN_STAT_UNLOAD;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		for (i = 0; i < ISDNLOOP_BCH; i++)
+			isdnloop_free_queue(card, i);
+		card = card->next;
+	}
+	card = cards;
+	while (card) {
+		struct sk_buff *skb;
+
+		last = card;
+		while ((skb = skb_dequeue(&card->dqueue)))
+			dev_kfree_skb(skb, FREE_WRITE);
+		card = card->next;
+		kfree(last);
+	}
+	printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n");
+}
+#endif
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.h linux/drivers/isdn/isdnloop/isdnloop.h
--- v2.0.35/linux/drivers/isdn/isdnloop/isdnloop.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdnloop/isdnloop.h	Sun Nov 15 10:33:01 1998
@@ -0,0 +1,145 @@
+/* $Id: isdnloop.h,v 1.1.2.1 1998/11/05 22:13:20 fritz Exp $
+
+ * Loopback lowlevel module for testing of linklevel.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.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)
+ * 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: isdnloop.h,v $
+ * Revision 1.1.2.1  1998/11/05 22:13:20  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.1  1997/03/24 23:02:05  fritz
+ * Added isdnloop driver.
+ *
+ */
+
+#ifndef isdnloop_h
+#define isdnloop_h
+
+#define ISDNLOOP_IOCTL_DEBUGVAR  0
+#define ISDNLOOP_IOCTL_ADDCARD   1
+#define ISDNLOOP_IOCTL_LEASEDCFG 2
+#define ISDNLOOP_IOCTL_STARTUP   3
+
+/* Struct for adding new cards */
+typedef struct isdnloop_cdef {
+	char id1[10];
+} isdnloop_cdef;
+
+/* Struct for configuring cards */
+typedef struct isdnloop_sdef {
+	int ptype;
+	char num[3][20];
+} isdnloop_sdef;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/config.h>
+#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/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>
+
+#endif                          /* __KERNEL__ */
+
+#define ISDNLOOP_FLAGS_B1ACTIVE 1	/* B-Channel-1 is open           */
+#define ISDNLOOP_FLAGS_B2ACTIVE 2	/* B-Channel-2 is open           */
+#define ISDNLOOP_FLAGS_RUNNING  4	/* Cards driver activated        */
+#define ISDNLOOP_FLAGS_RBTIMER  8	/* scheduling of B-Channel-poll  */
+#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle          */
+#define ISDNLOOP_TIMER_DCREAD (HZ/2)	/* D-Channel poll-cycle          */
+#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ)	/* Alert timeout                 */
+#define ISDNLOOP_MAX_SQUEUE 65536	/* Max. outstanding send-data    */
+#define ISDNLOOP_BCH 2          /* channels per card             */
+
+/*
+ * Per card driver data
+ */
+typedef struct isdnloop_card {
+	struct isdnloop_card *next;	/* Pointer to next device struct    */
+	struct isdnloop_card
+	*rcard[ISDNLOOP_BCH];   /* Pointer to 'remote' card         */
+	int rch[ISDNLOOP_BCH];  /* 'remote' channel                 */
+	int myid;               /* Driver-Nr. assigned by linklevel */
+	int leased;             /* Flag: This Adapter is connected  */
+	/*       to a leased line           */
+	int sil[ISDNLOOP_BCH];  /* SI's to listen for               */
+	char eazlist[ISDNLOOP_BCH][11];
+	/* EAZ's to listen for              */
+	char s0num[3][20];      /* 1TR6 base-number or MSN's        */
+	unsigned short flags;   /* Statusflags                      */
+	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        */
+	struct timer_list
+	 c_timer[ISDNLOOP_BCH]; /* Timer for Alerting               */
+	int l2_proto[ISDNLOOP_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  */
+	int optr;               /* Index to omsg-buffer             */
+	char omsg[60];          /* Internal buf for cmd-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[ISDNLOOP_BCH];	/* Byte-counters for B-Ch.-send     */
+	struct sk_buff_head
+	 bqueue[ISDNLOOP_BCH];  /* B-Channel queues                 */
+	struct sk_buff_head dqueue;	/* D-Channel queue                  */
+} isdnloop_card;
+
+/*
+ * Main driver data
+ */
+#ifdef __KERNEL__
+static isdnloop_card *cards = (isdnloop_card *) 0;
+static char *isdnloop_id = "\0";
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_PARM(isdnloop_id, "s");
+MODULE_PARM_DESC(isdnloop_id, "ID-String of first card");
+#endif
+#endif
+
+#endif                          /* __KERNEL__ */
+
+/* Utility-Macros */
+
+#define CID (card->interface.id)
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* isdnloop_h */
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/pcbit/callbacks.c linux/drivers/isdn/pcbit/callbacks.c
--- v2.0.35/linux/drivers/isdn/pcbit/callbacks.c	Mon Aug  4 17:34:00 1997
+++ linux/drivers/isdn/pcbit/callbacks.c	Sun Nov 15 10:33:01 1998
@@ -164,12 +164,18 @@
          *  ictl.num >= strlen() + strlen() + 5
          */
 
+	if (cbdata->data.setup.CallingPN == NULL)
+		strcpy(ictl.parm.setup.phone, "0");
+	else
 		strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+	if (cbdata->data.setup.CalledPN == NULL)
+		strcpy(ictl.parm.setup.eazmsn, "0");
+	else
 		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;
+	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.35/linux/drivers/isdn/pcbit/pcbit.h linux/drivers/isdn/pcbit/pcbit.h
--- v2.0.35/linux/drivers/isdn/pcbit/pcbit.h	Tue Apr 23 02:31:35 1996
+++ linux/drivers/isdn/pcbit/pcbit.h	Sun Nov 15 10:33:01 1998
@@ -98,7 +98,7 @@
 };
 
 #define STATS_TIMER (10*HZ)
-#define ERRTIME     (0.1*HZ)
+#define ERRTIME     (HZ/10)
 
 /* MRU */
 #define MAXBUFSIZE  1534
diff -u --recursive --new-file v2.0.35/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c
--- v2.0.35/linux/drivers/isdn/sc/interrupt.c	Sun Nov 15 10:49:35 1998
+++ linux/drivers/isdn/sc/interrupt.c	Sun Nov 15 10:33:01 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $
+ *  $Id: interrupt.c,v 1.3.2.1 1998/08/03 15:52:31 paul Exp $
  *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c505.h linux/drivers/net/3c505.h
--- v2.0.35/linux/drivers/net/3c505.h	Tue Aug 15 08:08:32 1995
+++ linux/drivers/net/3c505.h	Sun Nov 15 10:33:01 1998
@@ -61,7 +61,7 @@
 #define	DMA_BRST	0x01	/* DMA burst */
 
 /*
- * maximum amount of data data allowed in a PCB
+ * maximum amount of data allowed in a PCB
  */
 #define	MAX_PCB_DATA	62
 
diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.0.35/linux/drivers/net/3c509.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/3c509.c	Sun Nov 15 10:33:01 1998
@@ -1,8 +1,8 @@
 /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
 /*
-	Written 1993-1997 by Donald Becker.
+	Written 1993-1998 by Donald Becker.
 
-	Copyright 1994-1997 by Donald Becker.
+	Copyright 1994-1998 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,
@@ -33,15 +33,19 @@
 				delays
 		v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
 				other cleanups.  -djb
+		v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
+		v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
+		v1.15 1/31/98 Faster recovery for Tx errors. -djb
+		v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
 */
 
-static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.16 2/3/98 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
+static int max_interrupt_work = 10;
 
 #include <linux/module.h>
 
@@ -128,11 +132,11 @@
 	int head, size;
 	struct sk_buff *queue[SKB_QUEUE_SIZE];
 };
-static int id_port = 0x100;
+static int id_port = 0x110;		/* Start with 0x110 to avoid new sound cards.*/
 static struct device *el3_root_dev = NULL;
 
 static ushort id_read_eeprom(int index);
-static ushort read_eeprom(short ioaddr, int index);
+static ushort read_eeprom(int ioaddr, int index);
 static int el3_open(struct device *dev);
 static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
 static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
@@ -147,8 +151,8 @@
 int el3_probe(struct device *dev)
 {
 	short lrs_state = 0xff, i;
-	ushort ioaddr, irq, if_port;
-	short phys_addr[3];
+	int ioaddr, irq, if_port;
+	u16 phys_addr[3];
 	static int current_tag = 0;
 
 	/* First check all slots of the EISA bus.  The next slot address to
@@ -202,7 +206,7 @@
 	outb(0x02, 0x279);           /* Select PnP config control register. */
 	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) {
+	for ( ; id_port < 0x200; id_port += 0x10) {
 		if (check_region(id_port, 1))
 			continue;
 		outb(0x00, id_port);
@@ -246,18 +250,23 @@
 	}
 
 	{
-		unsigned short iobase = id_read_eeprom(8);
+		unsigned int iobase = id_read_eeprom(8);
 		if_port = iobase >> 14;
 		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
 	}
-	if (dev  &&  dev->irq > 1  &&  dev->irq < 16)
-		irq = dev->irq;
-	else
-		irq = id_read_eeprom(9) >> 12;
+	irq = id_read_eeprom(9) >> 12;
 
-	if (dev  &&  dev->base_addr != 0
-		&&  dev->base_addr != (unsigned short)ioaddr) {
-		return -ENODEV;
+	if (dev) {					/* Set passed-in IRQ or I/O Addr. */
+		if (dev->irq > 1  &&  dev->irq < 16)
+			irq = dev->irq;
+
+		if (dev->base_addr) {
+			if (dev->mem_end == 0x3c509 			/* Magic key */
+				&& dev->base_addr >= 0x200  &&  dev->base_addr <= 0x3e0)
+				ioaddr = dev->base_addr & 0x3f0;
+			else if (dev->base_addr != ioaddr)
+				return -ENODEV;
+		}
 	}
 
 	/* Set the adaptor tag so that the next card can be found. */
@@ -322,7 +331,7 @@
 /* Read a word from the EEPROM using the regular EEPROM access register.
    Assume that we are in register window zero.
  */
-static ushort read_eeprom(short ioaddr, int index)
+static ushort read_eeprom(int ioaddr, int index)
 {
 	outw(EEPROM_READ + index, ioaddr + 10);
 	/* Pause for at least 162 us. for the read to take place. */
@@ -419,7 +428,7 @@
 	/* Ack all pending events, and set active indicator mask. */
 	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
 		 ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull,
+	outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
 		 ioaddr + EL3_CMD);
 
 	if (el3_debug > 3)
@@ -521,7 +530,7 @@
 {
 	struct device *dev = (struct device *)dev_id;
 	int ioaddr, status;
-	int i = INTR_WORK;
+	int i = max_interrupt_work;
 
 	if (dev == NULL) {
 		printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
@@ -552,7 +561,7 @@
 			dev->tbusy = 0;
 			mark_bh(NET_BH);
 		}
-		if (status & (AdapterFailure | RxEarly | StatsFull)) {
+		if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
 			/* Handle all uncommon interrupts. */
 			if (status & StatsFull)				/* Empty statistics. */
 				update_stats(ioaddr, dev);
@@ -560,6 +569,18 @@
 				el3_rx(dev);
 				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
 			}
+			if (status & TxComplete) {			/* Really Tx error. */
+				struct el3_private *lp = (struct el3_private *)dev->priv;
+				short tx_status;
+				int i = 4;
+
+				while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+					if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
+					if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
+					if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
+					outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+				}
+			}
 			if (status & AdapterFailure) {
 				/* Adapter failure requires Rx reset and reinit. */
 				outw(RxReset, ioaddr + EL3_CMD);
@@ -653,6 +674,8 @@
 	while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
 		if (rx_status & 0x4000) { /* Error, update stats. */
 			short error = rx_status & 0x3800;
+
+			outw(RxDiscard, ioaddr + EL3_CMD);
 			lp->stats.rx_errors++;
 			switch (error) {
 			case 0x0000:		lp->stats.rx_over_errors++; break;
@@ -683,19 +706,21 @@
 					 (pkt_len + 3) >> 2);
 #endif
 
+				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				skb->protocol = eth_type_trans(skb,dev);
 				netif_rx(skb);
-				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				lp->stats.rx_packets++;
 				continue;
-			} else if (el3_debug)
-				printk(KERN_WARNING "%s: Couldn't allocate a sk_buff of size %d.\n",
+			}
+			outw(RxDiscard, ioaddr + EL3_CMD);
+			lp->stats.rx_dropped++;
+			if (el3_debug)
+				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
 					   dev->name, pkt_len);
 		}
-		lp->stats.rx_dropped++;
-		outw(RxDiscard, ioaddr + EL3_CMD);
+		inw(ioaddr + EL3_STATUS); 				/* Delay. */
 		while (inw(ioaddr + EL3_STATUS) & 0x1000)
-			printk(KERN_DEBUG "        Waiting for 3c509 to discard packet, status %x.\n",
+			printk("	Waiting for 3c509 to discard packet, status %x.\n",
 				   inw(ioaddr + EL3_STATUS) );
 	}
 
@@ -708,7 +733,7 @@
 static void
 set_multicast_list(struct device *dev)
 {
-	short ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
 	if (el3_debug > 1) {
 		static int old = 0;
 		if (old != dev->mc_count) {
@@ -766,7 +791,7 @@
 }
 
 #ifdef MODULE
-/* Parameter that may be passed into the module. */
+/* Parameters 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};
diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c515.c linux/drivers/net/3c515.c
--- v2.0.35/linux/drivers/net/3c515.c	Sun Nov 15 10:49:35 1998
+++ linux/drivers/net/3c515.c	Sun Nov 15 10:33:01 1998
@@ -39,7 +39,6 @@
 #define RX_RING_SIZE	16
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
-#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
diff -u --recursive --new-file v2.0.35/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.0.35/linux/drivers/net/3c59x.c	Sun Nov 15 10:49:36 1998
+++ linux/drivers/net/3c59x.c	Sun Nov 15 10:33:02 1998
@@ -20,9 +20,9 @@
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const int rx_copybreak = 200;
+static const rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const int mtu = 1500;
+static const mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
@@ -44,9 +44,7 @@
    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'. */
-#if YOU_ARE_BRAVER_THAN_ME
 #define VORTEX_BUS_MASTER
-#endif
 
 /* A few values that may be tweaked. */
 /* Time in jiffies before concluding the transmitter is hung. */
@@ -1761,7 +1759,8 @@
 		printk(KERN_DEBUG "  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) {
+	while ((--rx_work_limit >= 0) &&
+			((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 > 2)
@@ -1801,6 +1800,10 @@
 				void *temp;
 				/* Pass up the skbuff already on the Rx ring. */
 				skb = vp->rx_skbuff[entry];
+				if (skb == NULL) {
+					printk(KERN_WARNING "%s: in boomerang_rx -- attempt to use NULL skb caught\n", dev->name);
+					break;
+				}
 				vp->rx_skbuff[entry] = NULL;
 #if LINUX_VERSION_CODE >= 0x10300
 				temp = skb_put(skb, pkt_len);
@@ -1833,8 +1836,6 @@
 			vp->stats.rx_packets++;
 		}
 		entry = (++vp->cur_rx) % RX_RING_SIZE;
-		if (--rx_work_limit < 0)
-			break;
 	}
 	/* Refill the Rx ring buffers. */
 	for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
@@ -1842,8 +1843,10 @@
 		entry = vp->dirty_rx % RX_RING_SIZE;
 		if (vp->rx_skbuff[entry] == NULL) {
 			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
-			if (skb == NULL)
+			if (skb == NULL) {
+				printk(KERN_DEBUG "%s: in boomerang_rx -- could not allocate skbuff\n", dev->name);
 				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 */
@@ -1856,6 +1859,12 @@
 		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
 		outw(UpUnstall, ioaddr + EL3_CMD);
 	}
+
+	if (vp->dirty_rx >= RX_RING_SIZE ) {
+		vp->cur_rx -= RX_RING_SIZE;
+		vp->dirty_rx -= RX_RING_SIZE;
+	}
+
 	return 0;
 }
 
diff -u --recursive --new-file v2.0.35/linux/drivers/net/Changelog.tlan linux/drivers/net/Changelog.tlan
--- v2.0.35/linux/drivers/net/Changelog.tlan	Sun Nov 15 10:49:37 1998
+++ linux/drivers/net/Changelog.tlan	Sun Nov 15 10:33:02 1998
@@ -1,5 +1,8 @@
 TLan Device Driver change log.
 
+1.0	- Stopped ignoring devices that didn't has the bus master
+	  flag set.
+
 0.43	- Changed the id strings of the Compaq devices to their real
 	  names.
 	- Added 2 other Olicom devices with a patch provided by
@@ -66,7 +69,7 @@
 	  100Mbs should work now on 0xAE32.
 	- Fixed a small bug where heartbeat and PHY interrupts were
 	  always being enabled.
-	- Force the the driver into Unmanaged PHY mode for 0xF130 devices,
+	- Force the driver into Unmanaged PHY mode for 0xF130 devices,
 	  even if a managed (ie, the built-in one) PHY is detected.
 	- Moved the PHY initialization to after the onboard PHY is enabled,
 	  if selected.
diff -u --recursive --new-file v2.0.35/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.0.35/linux/drivers/net/Config.in	Sun Nov 15 10:49:37 1998
+++ linux/drivers/net/Config.in	Sun Nov 15 10:33:02 1998
@@ -46,7 +46,6 @@
   tristate 'Z8530 SCC KISS emulation driver for AX.25' CONFIG_SCC
   tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
   tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
-  tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC
 fi
 #
 #	Ethernet
@@ -83,9 +82,8 @@
     tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN
     tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139
     tristate 'SMC EPIC/100 (EtherPower II) support' CONFIG_EPIC
-    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-      tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
-   fi
+    tristate 'TI ThunderLAN support' CONFIG_TLAN
+    tristate 'VIA Rhine support' CONFIG_VIA_RHINE
   fi
   bool 'Other ISA cards' CONFIG_NET_ISA
   if [ "$CONFIG_NET_ISA" = "y" ]; then
@@ -147,3 +145,7 @@
   bool '  Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051
 fi
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER
+  tristate 'Red Creek Hardware VPN (EXPERIMENTAL)' CONFIG_RCPCI
+fi
diff -u --recursive --new-file v2.0.35/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.0.35/linux/drivers/net/Makefile	Sun Nov 15 10:49:37 1998
+++ linux/drivers/net/Makefile	Sun Nov 15 10:33:02 1998
@@ -3,7 +3,7 @@
 # Makefile for the Linux network (ethercard) device drivers.
 #
 
-# This will go away in some future future: hidden configuration files
+# This will go away in some future: hidden configuration files
 # are difficult for users to deal with.
 include CONFIG
 
@@ -53,6 +53,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SHAPER),y)
+L_OBJS += shaper.o
+else
+  ifeq ($(CONFIG_SHAPER),m)
+  M_OBJS += shaper.o
+  endif
+endif
+
 ifeq ($(CONFIG_SK_G16),y)
 L_OBJS += sk_g16.o
 endif
@@ -73,14 +81,6 @@
   endif
 endif
 
-ifeq ($(CONFIG_WIC),y)
-L_OBJS += wic.o
-else
-  ifeq ($(CONFIG_WIC),m)
-  M_OBJS += wic.o
-  endif
-endif
-
 ifeq ($(CONFIG_SMC9194),y)
 L_OBJS += smc9194.o
 else
@@ -319,6 +319,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_VIA_RHINE),y)
+L_OBJS += via-rhine.o
+else
+  ifeq ($(CONFIG_VIA_RHINE),m)
+  M_OBJS += via-rhine.o
+  endif
+endif
+
 ifeq ($(CONFIG_EEXPRESS),y)
 L_OBJS += eexpress.o
 else
@@ -659,10 +667,23 @@
   endif
 endif
 
+
+
+ifeq ($(CONFIG_RCPCI),y)
+L_OBJS += rcpci.o
+else
+  ifeq ($(CONFIG_RCPCI),m)
+  M_OBJS += rcpci.o
+  endif
+endif
+
 include $(TOPDIR)/Rules.make
 
 clean:
 	rm -f core *.o *.a *.s
+
+rcpci.o: rcpci45.o rcmtl.o
+	$(LD) -r -o rcpci.o rcpci45.o rcmtl.o
 
 wd.o:	wd.c CONFIG
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $<
diff -u --recursive --new-file v2.0.35/linux/drivers/net/README.eql linux/drivers/net/README.eql
--- v2.0.35/linux/drivers/net/README.eql	Mon May  6 02:26:07 1996
+++ linux/drivers/net/README.eql	Sun Nov 15 10:33:02 1998
@@ -257,9 +257,9 @@
   5.  Tester's Reports
 
   Some people have experimented with the eql device with newer kernels
-  kernels (than 1.1.75).  I have since updated the driver to patch
-  cleanly in newer kernels because of the removal of the old "slave-
-  balancing" driver config option.
+  (than 1.1.75).  I have since updated the driver to patch cleanly in
+  newer kernels because of the removal of the old "slave-balancing"
+  driver config option.
 
 
   o  icee from LinuxNET patched 1.1.86 without any rejects and was able
diff -u --recursive --new-file v2.0.35/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.0.35/linux/drivers/net/Space.c	Sun Nov 15 10:49:37 1998
+++ linux/drivers/net/Space.c	Sun Nov 15 10:33:02 1998
@@ -61,6 +61,7 @@
 extern int ewrk3_probe(struct device *);
 extern int de4x5_probe(struct device *);
 extern int el1_probe(struct device *);
+extern int via_rhine_probe(struct device *);
 #if	defined(CONFIG_WAVELAN)
 extern int wavelan_probe(struct device *);
 #endif	/* defined(CONFIG_WAVELAN) */
@@ -92,6 +93,10 @@
 extern int atp_init(struct device *);
 extern int de600_probe(struct device *);
 extern int de620_probe(struct device *);
+/* The shaper hook */
+extern int shaper_probe(struct device *);
+/* Red Creek PCI hook */
+extern int rcpci_probe(struct device *);
 
 static int
 ethif_probe(struct device *dev)
@@ -127,6 +132,9 @@
 #ifdef CONFIG_RTL8139
 	&& rtl8139_probe(dev)
 #endif
+#if defined(CONFIG_VIA_RHINE)
+	&& via_rhine_probe(dev)
+#endif
 #if defined(CONFIG_VORTEX)
 	&& tc59x_probe(dev)
 #endif
@@ -359,6 +367,20 @@
 #undef NEXT_DEV
 #define NEXT_DEV (&strip_bootstrap)
 #endif   /* STRIP */
+
+#if defined(CONFIG_SHAPER)
+static struct device shaper_bootstrap = {
+    "shaper", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, shaper_probe, };
+#undef NEXT_DEV
+#define NEXT_DEV (&shaper_bootstrap)
+#endif   /* SHAPER */
+
+#if defined(CONFIG_RCPCI)
+static struct device rcpci_bootstrap = {
+    "rcpci", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, rcpci_probe, };
+#undef NEXT_DEV
+#define NEXT_DEV (&rcpci_bootstrap)
+#endif   /* RCPCI */
 
 #if defined(CONFIG_PPP)
 extern int ppp_init(struct device *);
diff -u --recursive --new-file v2.0.35/linux/drivers/net/apricot.c linux/drivers/net/apricot.c
--- v2.0.35/linux/drivers/net/apricot.c	Thu Feb 29 21:50:42 1996
+++ linux/drivers/net/apricot.c	Sun Nov 15 10:33:02 1998
@@ -147,14 +147,14 @@
 };
 
 struct i596_private {
-    struct i596_scp scp;
-    struct i596_iscp iscp;
-    struct i596_scb scb;
-    struct i596_cmd set_add;
+    volatile struct i596_scp scp;
+    volatile struct i596_iscp iscp;
+    volatile struct i596_scb scb;
+    volatile struct i596_cmd set_add;
     char eth_addr[8];
-    struct i596_cmd set_conf;
+    volatile struct i596_cmd set_conf;
     char i596_config[16];
-    struct i596_cmd tdr;
+    volatile struct i596_cmd tdr;
     unsigned long stat;
     int last_restart;
     struct i596_rfd *rx_tail;
@@ -930,7 +930,7 @@
     while (lp->scb.status, lp->scb.command)
 	if (--boguscnt == 0)
 	{
-	    printk("%s: close timed timed out with status %4.4x, cmd %4.4x.\n",
+	    printk("%s: close timed out with status %4.4x, cmd %4.4x.\n",
 		   dev->name, lp->scb.status, lp->scb.command);
 	    break;
     	}
diff -u --recursive --new-file v2.0.35/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.0.35/linux/drivers/net/de4x5.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/de4x5.c	Sun Nov 15 10:33:02 1998
@@ -372,11 +372,43 @@
       0.534  24-Jan-98    Fix last (?) endian bug from 
                            <Geert.Uytterhoeven@cs.kuleuven.ac.be>
       0.535  21-Feb-98    Fix Ethernet Address PROM reset bug for DC21040.
+      0.5351  4-Oct-98    Atomicize assertion of dev->interrupt for SMP (not
+                           for Alpha arch.) from <lma@varesearch.com>
+			  Add TP, AUI and BNC cases to 21140m_autoconf() for
+			   case where a 21140 under SROM control uses, e.g. AUI
+			   from problem report by <delchini@lpnp09.in2p3.fr>
+			  Add MII parallel detection to 2114x_autoconf() for
+			   case where no autonegotiation partner exists from
+			   problem report by <mlapsley@ndirect.co.uk>.
+			  Add ability to force connection type directly even
+			   when using SROM control from problem report by
+			   <earl@exis.net>.
+			  Fix is_anc_capable() bug reported by 
+			   <Austin.Donnelly@cl.cam.ac.uk>.
+			  Fix type[13]_infoblock() bug: during MII search, PHY
+			   lp->rst not run because lp->ibn not initialised -
+			   from report & fix by <paubert@iram.es>.
+			  Fix probe bug with EISA & PCI cards present from
+			   report by <eirik@netcom.com>.
+			  Fix compiler problems associated with i386-string
+			   ops from multiple bug reports and temporary fix
+			   from <paubert@iram.es>.
+			  Add an_exception() for old ZYNX346 and fix compile
+			   warning on PPC & SPARC, from <ecd@skynet.be>.
+			  Fix lastPCI to correctly work with compiled in
+			   kernels and modules from bug report by 
+			   <Zlatko.Calusic@CARNet.hr> et al.
+			  Fix dc2114x_autoconf() to stop multiple messages
+			   when media is unconnected.
+			  Change dev->interrupt to lp->interrupt to ensure
+			   alignment for Alpha's and avoid their unaligned
+			   access traps. This flag is merely for log messages:
+			   should do something more definitive though...
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.535 1998/2/21 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.5351 1998/10/4 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -728,6 +760,7 @@
 
 struct de4x5_private {
     char adapter_name[80];                  /* Adapter name                 */
+    u_long interrupt;                       /* Aligned ISR flag             */
     struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring           */
     struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring           */
     struct sk_buff *tx_skb[NUM_TX_DESC];    /* TX skb for freeing when sent */
@@ -948,7 +981,8 @@
 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);
-#ifndef __sparc_v9__
+static int     an_exception(struct bus_type *lp);
+#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__)
 static void    eisa_probe(struct device *dev, u_long iobase);
 #endif
 static void    pci_probe(struct device *dev, u_long iobase);
@@ -999,12 +1033,15 @@
 #endif /* MODULE */
 
 static char name[DE4X5_NAME_LENGTH + 1];
-#ifndef __sparc_v9__
+#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__)
 static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
+static int lastEISA = 0;
+#else
+static int lastEISA = MAX_EISA_SLOTS;           /* Only PCI probes */
 #endif
 static int num_de4x5s = 0;
 static int cfrv = 0, useSROM = 0;
-static int lastEISA = 0, lastPCI = -1;
+static int lastPCI = -1;
 static struct device *lastModule = NULL;
 
 /*
@@ -1069,10 +1106,12 @@
 {
     u_long iobase = dev->base_addr;
 
-#ifndef __sparc_v9__
+#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__)
     eisa_probe(dev, iobase);
 #endif
-    pci_probe(dev, iobase);
+    if (lastEISA == MAX_EISA_SLOTS) {
+	pci_probe(dev, iobase);
+    }
     
     return (dev->priv ? 0 : -ENODEV);
 }
@@ -1169,21 +1208,15 @@
 	/*
 	** Choose correct autosensing in case someone messed up
 	*/
-	if ((lp->params.autosense & AUTO) || lp->useSROM) {
-	    lp->autosense = AUTO;
-	} else {
-	    if (lp->chipset != DC21140) {
-		if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) {
-		    lp->params.autosense = TP;
-		}
-		if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) {
-		    lp->params.autosense = BNC;
-		}
-		lp->autosense = lp->params.autosense & 0x001f;
-	    } else {
-		lp->autosense = lp->params.autosense & 0x00c0;
-	    }
-	}
+        lp->autosense = lp->params.autosense;
+        if (lp->chipset != DC21140) {
+            if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) {
+                lp->params.autosense = TP;
+            }
+            if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) {
+                lp->params.autosense = BNC;
+            }
+        }
 	lp->fdx = lp->params.fdx;
 	sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
 	
@@ -1347,7 +1380,7 @@
 
     dev->tbusy = 0;                         
     dev->start = 1;
-    dev->interrupt = UNMASK_INTERRUPTS;
+    lp->interrupt = UNMASK_INTERRUPTS;
     dev->trans_start = jiffies;
     
     START_DE4X5;
@@ -1467,7 +1500,7 @@
 }
 
 /* 
-** Writes a socket buffer address to the next available transmit descriptor
+** Writes a socket buffer address to the next available transmit descriptor.
 */
 static int
 de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
@@ -1491,12 +1524,12 @@
     sti();
 
     /* Test if cache is already locked - requeue skb if so */
-    if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) 
+    if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt) 
 	return -1;
 
     /* Transmit descriptor ring full or stale skb */
     if (dev->tbusy || lp->tx_skb[lp->tx_new]) {
-	if (dev->interrupt) {
+	if (lp->interrupt) {
 	    de4x5_putb_cache(dev, skb);          /* Requeue the buffer */
 	} else {
 	    de4x5_put_cache(dev, skb);
@@ -1506,7 +1539,7 @@
 	}
     } else if (skb->len > 0) {
 	/* If we already have stuff queued locally, use that first */
-	if (lp->cache.skb && !dev->interrupt) {
+	if (lp->cache.skb && !lp->interrupt) {
 	    de4x5_put_cache(dev, skb);
 	    skb = de4x5_get_cache(dev);
 	}
@@ -1563,14 +1596,14 @@
     lp = (struct de4x5_private *)dev->priv;
     iobase = dev->base_addr;
 	
-    if (dev->interrupt)
-      printk("%s: Re-entering the interrupt handler.\n", dev->name);
-	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
+
+    if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt))
+	printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
 #if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
     synchronize_irq();
 #endif
-    dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
 	sts = inl(DE4X5_STS);            /* Read IRQ status */
@@ -1608,7 +1641,7 @@
 	lp->cache.lock = 0;
     }
 
-    dev->interrupt = UNMASK_INTERRUPTS;
+    lp->interrupt = UNMASK_INTERRUPTS;
     ENABLE_IRQs;
     
     return;
@@ -1740,7 +1773,7 @@
 
     if (TX_BUFFS_AVAIL && dev->tbusy) {  /* Any resources available? */
 	dev->tbusy = 0;                  /* Clear TX busy flag */
-	if (dev->interrupt) mark_bh(NET_BH);
+	if (lp->interrupt) mark_bh(NET_BH);
     }
 	
     return 0;
@@ -2001,7 +2034,7 @@
     return;
 }
 
-#ifndef __sparc_v9__
+#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__)
 /*
 ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
 ** the motherboard. Upto 15 EISA devices are supported.
@@ -2069,7 +2102,7 @@
 
     return;
 }
-#endif                         /* !(__sparc_v9__) */
+#endif          /* !(__sparc_v9__) && !(__powerpc__) && !defined(__alpha__)*/
 
 /*
 ** PCI bus I/O device probe
@@ -2197,10 +2230,8 @@
 		dev->irq = irq;
 		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
 		    num_de4x5s++;
-		    if (loading_module) {
-			link_modules(lastModule, dev);
-			lastPCI = index;
-		    }
+		    lastPCI = index;
+		    if (loading_module) link_modules(lastModule, dev);
 		    return;
 		}
 	    } else if (ioaddr != 0) {
@@ -2210,7 +2241,7 @@
 	}
     }
 
-    if (loading_module) lastPCI = NO_MORE_PCI;
+    lastPCI = NO_MORE_PCI;
 
     return;
 }
@@ -2381,7 +2412,7 @@
     s32 imr;
     
     switch (lp->media) {
-      case INIT:
+    case INIT:
 	DISABLE_IRQs;
 	lp->tx_enable = NO;
 	lp->timeout = -1;
@@ -2399,36 +2430,36 @@
 	next_tick = dc21040_autoconf(dev);
 	break;
 	
-      case TP:
+    case TP:
 	next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI, 
 		                                         TP_SUSPECT, test_tp);
 	break;
 	
-      case TP_SUSPECT:
+    case TP_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf);
 	break;
 	
-      case BNC:
-      case AUI:
-      case BNC_AUI:
+    case BNC:
+    case AUI:
+    case BNC_AUI:
 	next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA, 
 		                                  BNC_AUI_SUSPECT, ping_media);
 	break;
 	
-      case BNC_AUI_SUSPECT:
+    case BNC_AUI_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf);
 	break;
 	
-      case EXT_SIA:
+    case EXT_SIA:
 	next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000, 
 		                              NC, EXT_SIA_SUSPECT, ping_media);
 	break;
 	
-      case EXT_SIA_SUSPECT:
+    case EXT_SIA_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf);
 	break;
 	
-      case NC:
+    case NC:
 	/* default to TP for all */
 	reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
 	if (lp->media != lp->c_media) {
@@ -2453,13 +2484,13 @@
     int linkBad;
 
     switch (lp->local_state) {
-      case 0:
+    case 0:
 	reset_init_sia(dev, csr13, csr14, csr15);
 	lp->local_state++;
 	next_tick = 500;
 	break;
 	    
-      case 1:
+    case 1:
 	if (!lp->tx_enable) {
 	    linkBad = fn(dev, timeout);
 	    if (linkBad < 0) {
@@ -2492,7 +2523,7 @@
     int linkBad;
 
     switch (lp->local_state) {
-      case 1:
+    case 1:
 	if (lp->linkOK) {
 	    lp->media = prev_state;
 	} else {
@@ -2501,7 +2532,7 @@
 	}
 	break;
 
-      case 2:
+    case 2:
 	linkBad = fn(dev, timeout);
 	if (linkBad < 0) {
 	    next_tick = linkBad & ~TIMER_CB;
@@ -2535,7 +2566,7 @@
     int next_tick = DE4X5_AUTOSENSE_MS;
     
     switch (lp->media) {
-      case INIT:
+    case INIT:
 	DISABLE_IRQs;
 	lp->tx_enable = NO;
 	lp->timeout = -1;
@@ -2555,7 +2586,7 @@
 	next_tick = dc21041_autoconf(dev);
 	break;
 	
-      case TP_NW:
+    case TP_NW:
 	if (lp->timeout < 0) {
 	    omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */
 	    outl(omr | OMR_FDX, DE4X5_OMR);
@@ -2575,7 +2606,7 @@
 	}
 	break;
 	
-      case ANS:
+    case ANS:
 	if (!lp->tx_enable) {
 	    irqs = STS_LNP;
 	    irq_mask = IMR_LPM;
@@ -2597,11 +2628,11 @@
 	}
 	break;
 	
-      case ANS_SUSPECT:
+    case ANS_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf);
 	break;
 	
-      case TP:
+    case TP:
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for TP */
@@ -2631,11 +2662,11 @@
 	}
 	break;
 	
-      case TP_SUSPECT:
+    case TP_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf);
 	break;
 	
-      case AUI:
+    case AUI:
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
@@ -2661,13 +2692,13 @@
 	}
 	break;
 	
-      case AUI_SUSPECT:
+    case AUI_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf);
 	break;
 	
-      case BNC:
+    case BNC:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
 		outl(omr & ~OMR_FDX, DE4X5_OMR);
@@ -2683,7 +2714,7 @@
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if (!lp->tx_enable) {
 		if ((sts = ping_media(dev, 3000)) < 0) {
 		    next_tick = sts & ~TIMER_CB;
@@ -2703,11 +2734,11 @@
 	}
 	break;
 	
-      case BNC_SUSPECT:
+    case BNC_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf);
 	break;
 	
-      case NC:
+    case NC:
 	omr = inl(DE4X5_OMR);    /* Set up full duplex for the autonegotiate */
 	outl(omr | OMR_FDX, DE4X5_OMR);
 	reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
@@ -2737,7 +2768,7 @@
     u_long imr, omr, iobase = dev->base_addr;
 
     switch(lp->media) {
-      case INIT: 
+    case INIT: 
         if (lp->timeout < 0) {
 	    DISABLE_IRQs;
 	    lp->tx_enable = FALSE;
@@ -2783,9 +2814,9 @@
 	}
 	break;
 	
-      case ANS:
+    case ANS:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
 		mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
 	    }
@@ -2803,7 +2834,7 @@
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
 		next_tick = sr & ~TIMER_CB;
 	    } else {
@@ -2831,7 +2862,7 @@
 	}
 	break;
 	
-      case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
+    case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
         if (lp->timeout < 0) {
 	    lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : 
 		                                  (~gep_rd(dev) & GEP_LNP));
@@ -2851,7 +2882,7 @@
 	}
 	break;
 	
-      case _100Mb:                               /* Set 100Mb/s */
+    case _100Mb:                               /* Set 100Mb/s */
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_100Mb;
@@ -2867,7 +2898,9 @@
 	}
 	break;
 	
-      case _10Mb:                                /* Set 10Mb/s */
+    case BNC:
+    case AUI:
+    case _10Mb:                                /* Set 10Mb/s */
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_10Mb;
@@ -2883,7 +2916,7 @@
 	}
 	break;
 	
-      case NC:
+    case NC:
         if (lp->media != lp->c_media) {
 	    de4x5_dbg_media(dev);
 	    lp->c_media = lp->media;
@@ -2919,33 +2952,54 @@
     int next_tick = DE4X5_AUTOSENSE_MS;
 
     switch (lp->media) {
-      case INIT:
+    case INIT:
         if (lp->timeout < 0) {
 	    DISABLE_IRQs;
 	    lp->tx_enable = FALSE;
 	    lp->linkOK = 0;
             lp->timeout = -1;
-	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
+	    de4x5_save_skbs(dev);            /* Save non transmitted skb's */
+	    if (lp->params.autosense & ~AUTO) {
+		srom_map_media(dev);         /* Fixed media requested      */
+		if (lp->media != lp->params.autosense) {
+		    lp->tcount++;
+		    lp->media = INIT;
+		    return next_tick;
+		}
+		lp->media = INIT;
+	    }
 	}
 	if ((next_tick = de4x5_reset_phy(dev)) < 0) {
 	    next_tick &= ~TIMER_CB;
 	} else {
-            lp->media = SPD_DET;
-	    if ((lp->infoblock_media == ANS) && 
+	    if (lp->autosense == _100Mb) {
+		lp->media = _100Mb;
+	    } else if (lp->autosense == _10Mb) {
+		lp->media = _10Mb;
+	    } else if (lp->autosense == TP) {
+		lp->media = TP;
+	    } else if (lp->autosense == BNC) {
+		lp->media = BNC;
+	    } else if (lp->autosense == AUI) {
+		lp->media = AUI;
+	    } else {
+		lp->media = SPD_DET;
+		if ((lp->infoblock_media == ANS) && 
 		                    ((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;
+		}
 	    }
 	    lp->local_state = 0;
 	    next_tick = dc2114x_autoconf(dev);
         }
 	break;
 	
-      case ANS:
+    case ANS:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
 		mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
 	    }
@@ -2963,7 +3017,7 @@
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
 		next_tick = sr & ~TIMER_CB;
 	    } else {
@@ -2985,15 +3039,15 @@
 		    }
 		}                       /* Auto Negotiation failed to finish */
 		next_tick = dc2114x_autoconf(dev);
-	    }                           /* Auto Negotiation failed to start */
+	    }                           /* Auto Negotiation failed to start  */
 	    break;
 	}
 	break;
 	
-      case AUI:
+    case AUI:
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
-		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
+		omr = inl(DE4X5_OMR);   /* Set up half duplex for AUI        */
 		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
@@ -3016,15 +3070,15 @@
 	}
 	break;
 	
-      case AUI_SUSPECT:
+    case AUI_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf);
 	break;
 	
-      case BNC:
+    case BNC:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
-		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
+		omr = inl(DE4X5_OMR);   /* Set up half duplex for BNC        */
 		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
@@ -3033,12 +3087,12 @@
 	    if (sts < 0) {
 		next_tick = sts & ~TIMER_CB;
 	    } else {
-		lp->local_state++;             /* Ensure media connected */
+		lp->local_state++;      /* Ensure media connected            */
 		next_tick = dc2114x_autoconf(dev);
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if (!lp->tx_enable) {
 		if ((sts = ping_media(dev, 3000)) < 0) {
 		    next_tick = sts & ~TIMER_CB;
@@ -3059,11 +3113,11 @@
 	}
 	break;
 	
-      case BNC_SUSPECT:
+    case BNC_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf);
 	break;
 	
-      case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
+    case SPD_DET:                       /* Choose 10Mb/s or 100Mb/s          */
 	  if (srom_map_media(dev) < 0) {
 	      lp->tcount++;
 	      lp->media = INIT;
@@ -3080,9 +3134,17 @@
 		  return PDET_LINK_WAIT;
 	      }
 	  } 
-	  if (((lp->media == _100Mb) && is_100_up(dev)) ||
-	      ((lp->media == _10Mb)  && is_10_up(dev)) ||
-	       (lp->media == BNC) || (lp->media == AUI)) {
+	  if (lp->media == ANS) {           /* Do MII parallel detection */
+	      if (is_spd_100(dev)) {
+		  lp->media = _100Mb;
+	      } else {
+		  lp->media = _10Mb;
+	      }
+	      next_tick = dc2114x_autoconf(dev);
+	  } else if (((lp->media == _100Mb) && is_100_up(dev)) ||
+		     (((lp->media == _10Mb) || (lp->media == TP) ||
+		       (lp->media == BNC)   || (lp->media == AUI)) && 
+		      is_10_up(dev))) {
 	      next_tick = dc2114x_autoconf(dev);
 	  } else {
 	      lp->tcount++;
@@ -3090,7 +3152,7 @@
 	  }
 	  break;
 	
-      case _10Mb:
+    case _10Mb:
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_10Mb;
@@ -3106,7 +3168,7 @@
 	}
 	break;
 
-      case _100Mb:
+    case _100Mb:
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_100Mb;
@@ -3122,7 +3184,7 @@
 	}
 	break;
 
-      default:
+    default:
 	lp->tcount++;
 printk("Huh?: media:%02x\n", lp->media);
 	lp->media = INIT;
@@ -3492,7 +3554,7 @@
     if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
     } else if ((lp->chipset & ~0x00ff) == DC2114x) {
-	return (inl(DE4X5_SISR) & SISR_LPN) >> 11;
+	return (inl(DE4X5_SISR) & SISR_LPN) >> 12;
     } else {
 	return 0;
     }
@@ -3905,7 +3967,7 @@
 static int
 EISA_signature(char *name, s32 eisa_id)
 {
-    c_char *signatures[] = DE4X5_SIGNATURE;
+    static c_char *signatures[] = DE4X5_SIGNATURE;
     char ManCode[DE4X5_STRLEN];
     union {
 	s32 ID;
@@ -3940,7 +4002,7 @@
 static int
 PCI_signature(char *name, struct bus_type *lp)
 {
-    c_char *de4x5_signatures[] = DE4X5_SIGNATURE;
+    static c_char *de4x5_signatures[] = DE4X5_SIGNATURE;
     int i, status = 0, siglen = sizeof(de4x5_signatures)/sizeof(c_char *);
     
     if (lp->chipset == DC21040) {
@@ -4119,6 +4181,21 @@
     /* If possible, try to fix a broken card - SMC only so far */
     srom_repair(dev, broken);
 
+#ifdef CONFIG_PMAC
+    /* 
+    ** If the address starts with 00 a0, we have to bit-reverse
+    ** each byte of the address.
+    */
+    if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0xa0) {
+	for (i = 0; i < ETH_ALEN; ++i) {
+	    int x = dev->dev_addr[i];
+	    x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4);
+	    x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2);
+	    dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1);
+	}
+    }
+#endif /* CONFIG_PMAC */
+
     /* Test for a bad enet address */
     status = test_bad_enet(dev, status);
 
@@ -4198,7 +4275,9 @@
 		if (dev->dev_addr[i] != 0) break;
 	    }
 	    for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
-	    dev->irq = last.irq;
+	    if (!an_exception(lp)) {
+		dev->irq = last.irq;
+	    }
 
 	    status = 0;
 	}
@@ -4213,6 +4292,20 @@
 }
 
 /*
+** List of board exceptions with correctly wired IRQs
+*/
+static int
+an_exception(struct bus_type *lp)
+{
+    if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) && 
+	(*(u_short *)lp->srom.sub_system_id == 0x95e0)) {
+	return -1;
+    }
+
+    return 0;
+}
+
+/*
 ** SROM Read
 */
 static short
@@ -4658,6 +4751,7 @@
 
     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);
@@ -4737,6 +4831,7 @@
 
     p += 2;
     if (lp->state == INITIALISED) {
+        lp->ibn = 3;
         lp->active = *p++;
 	lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1);
 	lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1);
@@ -5484,24 +5579,24 @@
     } tmp;
     
     switch(ioc->cmd) {
-      case DE4X5_GET_HWADDR:           /* Get the hardware address */
+    case DE4X5_GET_HWADDR:           /* Get the hardware address */
 	ioc->len = ETH_ALEN;
 	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
 	if (status)
-	  break;
+	    break;
 	for (i=0; i<ETH_ALEN; i++) {
 	    tmp.addr[i] = dev->dev_addr[i];
 	}
 	copy_to_user(ioc->data, tmp.addr, ioc->len);
 	
 	break;
-      case DE4X5_SET_HWADDR:           /* Set the hardware address */
+    case DE4X5_SET_HWADDR:           /* Set the hardware address */
 	status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN);
 	if (status)
-	  break;
+	    break;
 	status = -EPERM;
 	if (!suser())
-	  break;
+	    break;
 	status = 0;
 	copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
 	for (i=0; i<ETH_ALEN; i++) {
@@ -5517,7 +5612,7 @@
 	dev->tbusy = 0;                              /* Unlock the TX ring */
 	
 	break;
-      case DE4X5_SET_PROM:             /* Set Promiscuous Mode */
+    case DE4X5_SET_PROM:             /* Set Promiscuous Mode */
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PR;
@@ -5528,7 +5623,7 @@
 	}
 	
 	break;
-      case DE4X5_CLR_PROM:             /* Clear Promiscuous Mode */
+    case DE4X5_CLR_PROM:             /* Clear Promiscuous Mode */
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr &= ~OMR_PR;
@@ -5539,11 +5634,11 @@
 	}
 	
 	break;
-      case DE4X5_SAY_BOO:              /* Say "Boo!" to the kernel log file */
+    case DE4X5_SAY_BOO:              /* Say "Boo!" to the kernel log file */
 	printk("%s: Boo!\n", dev->name);
 	
 	break;
-      case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
+    case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PM;
@@ -5553,18 +5648,18 @@
 	}
 	
 	break;
-      case DE4X5_GET_STATS:            /* Get the driver statistics */
+    case DE4X5_GET_STATS:            /* Get the driver statistics */
 	ioc->len = sizeof(lp->pktStats);
 	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
 	if (status)
-	  break;
+	    break;
 	
 	cli();
 	copy_to_user(ioc->data, &lp->pktStats, ioc->len); 
 	sti();
 	
 	break;
-      case DE4X5_CLR_STATS:            /* Zero out the driver statistics */
+    case DE4X5_CLR_STATS:            /* Zero out the driver statistics */
 	if (suser()) {
 	    cli();
 	    memset(&lp->pktStats, 0, sizeof(lp->pktStats));
@@ -5574,14 +5669,14 @@
 	}
 	
 	break;
-      case DE4X5_GET_OMR:              /* Get the OMR Register contents */
+    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))) {
 	    copy_to_user(ioc->data, tmp.addr, 1);
 	}
 	
 	break;
-      case DE4X5_SET_OMR:              /* Set the OMR Register contents */
+    case DE4X5_SET_OMR:              /* Set the OMR Register contents */
 	if (suser()) {
 	    if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
 		copy_from_user(tmp.addr, ioc->data, 1);
@@ -5592,7 +5687,7 @@
 	}
 	
 	break;
-      case DE4X5_GET_REG:              /* Get the DE4X5 Registers */
+    case DE4X5_GET_REG:              /* Get the DE4X5 Registers */
 	j = 0;
 	tmp.lval[0] = inl(DE4X5_STS); j+=4;
 	tmp.lval[1] = inl(DE4X5_BMR); j+=4;
@@ -5700,7 +5795,7 @@
 	
 	break;
 */
-      default:
+    default:
 	status = -EOPNOTSUPP;
     }
     
@@ -5781,16 +5876,17 @@
 static int
 count_adapters(void)
 {
-    int i, j;
-    char name[DE4X5_STRLEN];
+    int i, j=0;
     u_char pb, dev_fn, dev_num;
     u_short dev_id, vendor;
     u_int class = DE4X5_CLASS_CODE;
     u_int device;
-#ifndef __sparc_v9__
+
+#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__)
+    char name[DE4X5_STRLEN];
     u_long iobase = 0x1000;
 
-    for (j=0, i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
+    for (i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) j++;
     }
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/net/eepro.c linux/drivers/net/eepro.c
--- v2.0.35/linux/drivers/net/eepro.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/eepro.c	Sun Nov 15 10:33:02 1998
@@ -1,19 +1,15 @@
 /* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */
 /*
-	Written 1994, 1995,1996 by Bao C. Ha.
-	Last Update, 2/7/98, Phillip R. Jaenke
+	Written 1994-1998 by Bao C. Ha.
 
-	Copyright (C) 1994, 1995,1996 by Bao C. Ha.
+	Copyright (C) 1994-1998 by Bao C. Ha.
 
 	This software may be used and distributed
 	according to the terms of the GNU Public License,
 	incorporated herein by reference.
 
-	The author may be reached at bao.ha@srs.gov 
-	or 418 Hastings Place, Martinez, GA 30907.
-
-	Phillip R. Jaenke may be reached at prj@nls.net
-	or P.O. Box 413, Twinsburg, OH 44087, ATTN: Linux
+	The author may be reached at bao@hacom.net 
+	or Hacom, 2477 Wrightsboro Rd., Augusta, GA 30904.
 
 	Things remaining to do:
 	Better record keeping of errors.
@@ -27,9 +23,19 @@
 	This is a compatibility hardware problem.
 
 	Versions:
-	
-	0.10	Added several I/O addresses previously not included.
-		(PRJ, 2/7/98)
+
+	0.10c	Some cosmetic changes. (9/28/98, BCH)
+
+        0.10b	Should work now with (some) Pro/10+. At least for 
+        	me (and my two cards) it does. _No_ guarantee for 
+        	function with non-Pro/10+ cards! (don't have any)
+        	(RMC, 9/11/96)
+
+	0.10	Added support for the Etherexpress Pro/10+.  The
+		IRQ map was changed significantly from the old
+		pro/10.  The new interrupt map was provided by
+		Rainer M. Canavan (Canavan@Zeus.cs.bonn.edu).
+		(BCH, 9/3/96)
 
 	0.09	Fixed a race condition in the transmit algorithm,
 		which causes crashes under heavy load with fast
@@ -67,7 +73,7 @@
 */
 
 static const char *version =
-	"eepro.c: v0.10 7/31/96 Bao C. Ha (bao.ha@srs.gov)\n";
+	"eepro.c: v0.10c 9/28/98 Bao C. Ha (bao@hacom.net)\n";
 
 #include <linux/module.h>
 
@@ -113,22 +119,26 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
-
 /* First, a few definitions that the brave might change. */
+
 /* A zero-terminated list of I/O addresses to be probed. */
 static unsigned int eepro_portlist[] =
-   { 0x200, 0x240, 0x260, 0x280, 0x2C0, 0x300, 0x310, 0x320, 0x340, 0x360, 0};
+   { 0x300, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0};
 
 /* use 0 for production, 1 for verification, >2 for debug */
+
 #ifndef NET_DEBUG
-#define NET_DEBUG 3
+#define NET_DEBUG 1
 #endif
+
 static unsigned int net_debug = NET_DEBUG;
 
 /* The number of low I/O ports used by the ethercard. */
+
 #define EEPRO_IO_EXTENT	16
 
 /* Different 82595 chips */
+
 #define	LAN595		0
 #define	LAN595TX	1
 #define	LAN595FX	2
@@ -140,18 +150,29 @@
 	unsigned tx_start; /* start of the transmit chain */
 	int tx_last;  /* pointer to last packet in the transmit chain */
 	unsigned tx_end;   /* end of the transmit chain (plus 1) */
-	int eepro;	/* a flag, TRUE=1 for the EtherExpress Pro/10,
-			   FALSE = 0 for other 82595-based lan cards. */
+	int eepro;	/* 1 for the EtherExpress Pro/10,
+			   2 for the EtherExpress Pro/10+,
+			   0 for other 82595-based lan cards. */
 	int version;	/* a flag to indicate if this is a TX or FX
 				   version of the 82595 chip. */
 	int stepping;
 };
 
 /* The station (ethernet) address prefix, used for IDing the board. */
-#define SA_ADDR0 0x00
+
+#define SA_ADDR0 0x00	/* Etherexpress Pro/10 */
 #define SA_ADDR1 0xaa
 #define SA_ADDR2 0x00
 
+#define SA2_ADDR0 0x00	/* Etherexpress Pro/10+ */
+#define SA2_ADDR1 0xa0
+#define SA2_ADDR2 0xc9
+
+#define SA3_ADDR0 0x00	/* more Etherexpress Pro/10+ */
+#define SA3_ADDR1 0xaa
+#define SA3_ADDR2 0x00
+#define SA3_ADDR3 0xc9
+
 /* Index to functions, as function prototypes. */
 
 extern int eepro_probe(struct device *dev);	
@@ -200,14 +221,18 @@
 buffer (transmit-buffer = 32K - receive-buffer).
 
 */
+
 #define	RAM_SIZE	0x8000
 #define	RCV_HEADER	8
 #define RCV_RAM         0x6000  /* 24KB default for RCV buffer */
 #define RCV_LOWER_LIMIT 0x00    /* 0x0000 */
+
 /* #define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) */    /* 0x5ffe */
 #define RCV_UPPER_LIMIT (((rcv_ram) - 2) >> 8)   
+
 /* #define XMT_RAM         (RAM_SIZE - RCV_RAM) */    /* 8KB for XMT buffer */
 #define XMT_RAM         (RAM_SIZE - (rcv_ram))    /* 8KB for XMT buffer */
+
 /* #define XMT_LOWER_LIMIT (RCV_RAM >> 8) */  /* 0x6000 */
 #define XMT_LOWER_LIMIT ((rcv_ram) >> 8) 
 #define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8)   /* 0x7ffe */
@@ -228,6 +253,7 @@
 #define	BANK2_SELECT	0x80		
 
 /* Bank 0 registers */
+
 #define	COMMAND_REG	0x00	/* Register 0 */
 #define	MC_SETUP	0x03
 #define	XMT_CMD		0x04
@@ -263,6 +289,7 @@
 #define	IO_PORT_32_BIT	0x0c
 
 /* Bank 1 registers */
+
 #define	REG1	0x01
 #define	WORD_WIDTH	0x02
 #define	INT_ENABLE	0x80
@@ -273,6 +300,7 @@
 #define	XMT_UPPER_LIMIT_REG	0x0b
 
 /* Bank 2 registers */
+
 #define	XMT_Chain_Int	0x20	/* Interrupt at the end of the transmit chain */
 #define	XMT_Chain_ErrStop	0x40 /* Interrupt at the end of the chain even if there are errors */
 #define	RCV_Discard_BadFrame	0x80 /* Throw bad frames away, and continue to receive others */
@@ -285,7 +313,7 @@
 #define	REG13		0x0d
 #define	FDX		0x00
 #define	A_N_ENABLE	0x02
-	
+
 #define	I_ADD_REG0	0x04
 #define	I_ADD_REG1	0x05
 #define	I_ADD_REG2	0x06
@@ -299,20 +327,26 @@
 #define EEDI 0x04
 #define EEDO 0x08
 
+/* Check for a network adaptor of this type, and return '0' if one exists.
 
-/* Check for a network adaptor 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.
    If dev->base_addr == 2, allocate space for the device and return success
    (detachable devices only).
+
    */
+
 #ifdef HAVE_DEVLIST
+
 /* Support for a alternate probe manager, which will eliminate the
    boilerplate below. */
+
 struct netdev_entry netcard_drv =
 {"eepro", eepro_probe1, EEPRO_IO_EXTENT, eepro_portlist};
+
 #else
-int
+
+int 
 eepro_probe(struct device *dev)
 {
 	int i;
@@ -327,6 +361,7 @@
 		int ioaddr = eepro_portlist[i];
 		if (check_region(ioaddr, EEPRO_IO_EXTENT))
 			continue;
+
 		if (eepro_probe1(dev, ioaddr) == 0)
 			return 0;
 	}
@@ -339,24 +374,23 @@
    probes on the ISA bus.  A good device probes avoids doing writes, and
    verifies that the correct device exists and functions.  */
 
-int eepro_probe1(struct device *dev, short ioaddr)
+int 
+eepro_probe1(struct device *dev, short ioaddr)
 {
 	unsigned short station_addr[6], id, counter;
 	int i;
-	int eepro;	/* a flag, TRUE=1 for the EtherExpress Pro/10,
-			   FALSE = 0 for other 82595-based lan cards. */
+	int eepro;
 	const char *ifmap[] = {"AUI", "10Base2", "10BaseT"};
 	enum iftype { AUI=0, BNC=1, TPE=2 };
 
 	/* Now, we are going to check for the signature of the
 	   ID_REG (register 2 of bank 0) */
-
 	if (((id=inb(ioaddr + ID_REG)) & ID_REG_MASK) == ID_REG_SIG) {
 
 		/* We seem to have the 82595 signature, let's
 		   play with its counter (last 2 bits of
 		   register 2 of bank 0) to be sure. */
-	
+
 		counter = (id & R_ROBIN_BITS);	
 		if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) == 
 			(counter + 0x40)) {
@@ -372,44 +406,54 @@
 
 			/* Check the station address for the manufacturer's code */
 
-			if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) {
-				eepro = 0;
-				printk("%s: Intel 82595-based lan card at %#x,", 
+			if ((station_addr[2] == 0x00aa) && (station_addr[1]!= 0x00c9)) {
+				eepro = 1;
+				printk("%s: Intel EtherExpress Pro/10 ISA at %#x,", 
+					dev->name, ioaddr);
+			} else
+			if ( (station_addr[2] == 0x00a0)
+			     || ((station_addr[2] == 0x00aa) && (station_addr[1] == 0x00c9) )) {
+				eepro = 2;
+				printk("%s: Intel EtherExpress Pro/10+ ISA\n at %#x,", 
 					dev->name, ioaddr);
 			}
 			else {
-				eepro = 1;
-				printk("%s: Intel EtherExpress Pro/10 at %#x,", 
+				eepro = 0;
+				printk("%s: Intel 82595-based lan card at %#x,", 
 					dev->name, ioaddr);
 			}
 
    			/* Fill in the 'dev' fields. */
 			dev->base_addr = ioaddr;
-			
+
 			for (i=0; i < 6; i++) {
 				dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i];
 				printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
 			}
-			
+
 			if ((dev->mem_end & 0x3f) < 3 ||	/* RX buffer must be more than 3K */
 				(dev->mem_end & 0x3f) > 29)	/* and less than 29K */
 				dev->mem_end = RCV_RAM;		/* or it will be set to 24K */
 			else dev->mem_end = 1024*dev->mem_end;  /* Maybe I should shift << 10 */
 
 			/* From now on, dev->mem_end contains the actual size of rx buffer */
-			
+
 			if (net_debug > 3)
 				printk(", %dK RCV buffer", (int)(dev->mem_end)/1024);
-				
+
 			outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 			id = inb(ioaddr + REG3);
 			if (id & TPE_BIT)
 				dev->if_port = TPE;
 			else dev->if_port = BNC;
 
+			if (net_debug>3) 
+				printk("id: %x\n", id);
+
 			if (dev->irq < 2 && eepro) {
 				i = read_eeprom(ioaddr, 1);
-				switch (i & 0x07) {
+				if (eepro == 1)
+				   switch (i & 0x07) {
 					case 0:	dev->irq = 9; break;
 					case 1:	dev->irq = 3; break;
 					case 2:	dev->irq = 5; break;
@@ -419,6 +463,16 @@
 						printk(" illegal interrupt vector stored in EEPROM.\n");
 						return ENODEV;
 					}
+				else switch (i & 0x07) {
+					case 0:	dev->irq = 3; break;
+					case 1:	dev->irq = 4; break;
+					case 2:	dev->irq = 5; break;
+					case 3:	dev->irq = 7; break;
+					case 4:	dev->irq = 9; break;
+					case 5:	dev->irq = 10; break;
+					case 6:	dev->irq = 11; break;
+					case 7:	dev->irq = 12; break;
+					}
 				}
 			else if (dev->irq == 2)
 				dev->irq = 9;
@@ -432,7 +486,7 @@
 				}
 			}
 			else printk(", %s.\n", ifmap[dev->if_port]);
-			
+
 			if ((dev->mem_start & 0xf) > 0)	/* I don't know if this is */
 				net_debug = dev->mem_start & 7; /* still useful or not */
 
@@ -486,9 +540,12 @@
    */
 
 static char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1};
-static int	eepro_grab_irq(struct device *dev)
+static char irqrmap2[] = {-1,-1,4,0,1,2,-1,3,-1,4,5,6,7,-1,-1,-1};
+
+static int	
+eepro_grab_irq(struct device *dev)
 {
-	int irqlist[] = { 5, 9, 10, 11, 4, 3, 0};	
+	int irqlist[] = { 3, 4, 5, 7, 9, 10, 11, 12 };	
 	int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr;
 
 	outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
@@ -496,32 +553,28 @@
 	/* Enable the interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
 	outb(temp_reg | INT_ENABLE, ioaddr + REG1); 
-	
+
 	outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
 
 	/* clear all interrupts */
 	outb(ALL_MASK, ioaddr + STATUS_REG); 
+
 	/* Let EXEC event to interrupt */
 	outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG); 
 
 	do {
 		outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
-
 		temp_reg = inb(ioaddr + INT_NO_REG);
 		outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG); 
-
 		outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
-
 		if (request_irq (*irqp, NULL, 0, "bogus", NULL) != EBUSY) {
 			/* Twinkle the interrupt, and check if it's seen */
 			autoirq_setup(0);
-
 			outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */
 				
 			if (*irqp == autoirq_report(2) &&  /* It's a good IRQ line */
 				(request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro", NULL) == 0)) 
 					break;
-
 			/* clear all interrupts */
 			outb(ALL_MASK, ioaddr + STATUS_REG); 
 		}
@@ -532,7 +585,6 @@
 	/* Disable the physical interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
 	outb(temp_reg & 0x7f, ioaddr + REG1); 
-
 	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
 
 	/* Mask all the interrupts. */
@@ -554,10 +606,27 @@
 	if (net_debug > 3)
 		printk("eepro: entering eepro_open routine.\n");
 
-	if (dev->dev_addr[0] == SA_ADDR0 &&
+	if ((dev->dev_addr[0] == SA_ADDR0 &&
 			dev->dev_addr[1] == SA_ADDR1 &&
-			dev->dev_addr[2] == SA_ADDR2)
-		lp->eepro = 1; /* Yes, an Intel EtherExpress Pro/10 */
+			dev->dev_addr[2] == SA_ADDR2)&&
+			(dev->dev_addr[3] != SA3_ADDR3))
+		{ 
+			lp->eepro = 1;
+			if (net_debug > 3) printk("p->eepro = 1;\n"); 
+		}  /* Yes, an Intel EtherExpress Pro/10 */
+
+	else if ((dev->dev_addr[0] == SA2_ADDR0 &&
+			dev->dev_addr[1] == SA2_ADDR1 &&
+			dev->dev_addr[2] == SA2_ADDR2)||
+		(dev->dev_addr[0] == SA3_ADDR0 &&
+			dev->dev_addr[1] == SA3_ADDR1 &&
+			dev->dev_addr[2] == SA3_ADDR2 &&
+			dev->dev_addr[3] == SA3_ADDR3))
+		{
+		  	lp->eepro = 2; /* Yes, an Intel EtherExpress Pro/10+ */
+		  	if (net_debug > 3) printk("p->eepro = 2;\n"); 
+		}
+       
 	else lp->eepro = 0; /* No, it is a generic 82585 lan card */
 
 	/* Get the interrupt vector for the 82595 */	
@@ -571,15 +640,12 @@
 		return -EAGAIN;
 
 	/* Initialize the 82595. */
-
 	outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 	temp_reg = inb(ioaddr + EEPROM_REG);
-
 	lp->stepping = temp_reg >> 5;	/* Get the stepping number of the 595 */
 	
 	if (net_debug > 3)
 		printk("The stepping of the 82595 is %d\n", lp->stepping);
-
 	if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
 		outb(temp_reg & 0xef, ioaddr + EEPROM_REG);
 	for (i=0; i < 6; i++) 
@@ -588,19 +654,31 @@
 	temp_reg = inb(ioaddr + REG1);    /* Setup Transmit Chaining */
 	outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */
 		| RCV_Discard_BadFrame, ioaddr + REG1);  
-
 	temp_reg = inb(ioaddr + REG2); /* Match broadcast */
 	outb(temp_reg | 0x14, ioaddr + REG2);
-
 	temp_reg = inb(ioaddr + REG3);
 	outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */
 
 	/* Set the receiving mode */
 	outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+
+	/* Set the interrupt vector */	
+	temp_reg = inb(ioaddr + INT_NO_REG);
 	
+	if (lp->eepro == 2)
+		outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG); 
+	else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); 
+    
 	temp_reg = inb(ioaddr + INT_NO_REG);
-	outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); 
-
+    
+	if (lp->eepro == 2)
+       		outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG);
+    	else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+    
+	if (net_debug > 3)
+       		printk("eepro_open: content of INT Reg is %x\n", temp_reg);
+       
+       
 	/* Initialize the RCV and XMT upper and lower limits */
 	outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG); 
 	outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG); 
@@ -610,25 +688,26 @@
 	/* Enable the interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
 	outb(temp_reg | INT_ENABLE, ioaddr + REG1); 
-
 	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
-
+	
 	/* Let RX and TX events to interrupt */
 	outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
+	
 	/* clear all interrupts */
 	outb(ALL_MASK, ioaddr + STATUS_REG); 
-
+	
 	/* Initialize RCV */
 	outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR); 
 	lp->rx_start = (RCV_LOWER_LIMIT << 8) ;
 	outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP); 
-
+	
 	/* Initialize XMT */
 	outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR); 
-
+	
 	/* Check for the i82595TX and i82595FX */
 	old8 = inb(ioaddr + 8);
 	outb(~old8, ioaddr + 8);
+	
 	if ((temp_reg = inb(ioaddr + 8)) == old8) {
 		if (net_debug > 3)
 			printk("i82595 detected!\n");
@@ -639,45 +718,55 @@
 		outb(old8, ioaddr + 8);
 		old9 = inb(ioaddr + 9);
 		outb(~old9, ioaddr + 9);
-		if ((temp_reg = inb(ioaddr + 9)) == ~old9) {
+	
+		if (((temp_reg = inb(ioaddr + 9)) == ( (~old9)&0xff) )) {
 			enum iftype { AUI=0, BNC=1, TPE=2 };
-			if (net_debug > 3)
+	
+			if (net_debug > 3) {
+			        printk("temp_reg: %#x  ~old9: %#x\n",temp_reg, ~old9);
 				printk("i82595FX detected!\n");
+			}
+	
 			lp->version = LAN595FX;
 			outb(old9, ioaddr + 9);
+	
 			if (dev->if_port != TPE) {	/* Hopefully, this will fix the
 							problem of using Pentiums and
 							pro/10 w/ BNC. */
 				outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 				temp_reg = inb(ioaddr + REG13);
+	
 				/* disable the full duplex mode since it is not
 				applicable with the 10Base2 cable. */
 				outb(temp_reg & ~(FDX | A_N_ENABLE), REG13);
 				outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
 			}
 		}
-		else if (net_debug > 3)
+		else if (net_debug > 3) {
+		        printk("temp_reg: %#x  ~old9: %#x\n",temp_reg,((~old9)&0xff));
 			printk("i82595TX detected!\n");
+		}
 	}
 	
 	outb(SEL_RESET_CMD, ioaddr);
+	
 	/* We are supposed to wait for 2 us after a SEL_RESET */
 	SLOW_DOWN_IO;
 	SLOW_DOWN_IO;	
-
+	
 	lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */
 	lp->tx_last = 0;  
 	
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
-
+	
 	if (net_debug > 3)
 		printk("eepro: exiting eepro_open routine.\n");
-
+	
 	outb(RCV_ENABLE_CMD, ioaddr);
-
 	MOD_INC_USE_COUNT;
+	
 	return 0;
 }
 
@@ -694,64 +783,68 @@
 	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 < 40)
 			return 1;
+	
 		if (net_debug > 1)
 			printk("%s: transmit timed out, %s?\n", dev->name,
 				   "network cable problem");
+	
 		lp->stats.tx_errors++;
-
+	
 		/* Try to restart the adaptor. */
 		outb(SEL_RESET_CMD, ioaddr); 
+	
 		/* We are supposed to wait for 2 us after a SEL_RESET */
 		SLOW_DOWN_IO;
 		SLOW_DOWN_IO;
-
+	
 		/* Do I also need to flush the transmit buffers here? YES? */
 		lp->tx_start = lp->tx_end = rcv_ram; 
 		lp->tx_last = 0;
 	
 		dev->tbusy=0;
 		dev->trans_start = jiffies;
-
 		outb(RCV_ENABLE_CMD, ioaddr);
-
 	}
-
+	
 	/* 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. */
+	
 	if (set_bit(0, (void*)&dev->tbusy) != 0)
 		printk("%s: Transmitter access conflict.\n", dev->name);
 	else {
 		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
 		unsigned char *buf = skb->data;
-
 		hardware_send_packet(dev, buf, length);
 		dev->trans_start = jiffies;
 	}
-
+	
 	dev_kfree_skb (skb, FREE_WRITE);
-
+	
 	/* You might need to clean up and record Tx statistics here. */
 	/* lp->stats.tx_aborted_errors++; */
-
+	
 	if (net_debug > 5)
 		printk("eepro: exiting eepro_send_packet routine.\n");
 	
 	return 0;
 }
 
-
 /*	The typical workload of the driver:
 	Handle the network interface interrupts. */
+
 static void
 eepro_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
@@ -765,38 +858,35 @@
 		printk ("eepro_interrupt(): irq %d for unknown device.\n", irq);
 		return;
 	}
+	
 	dev->interrupt = 1;
 	
 	ioaddr = dev->base_addr;
-
+	
 	do { 
 		status = inb(ioaddr + STATUS_REG);
 		
 		if (status & RX_INT) {
 			if (net_debug > 4)
 				printk("eepro: packet received interrupt.\n");
-
 			/* Acknowledge the RX_INT */
 			outb(RX_INT, ioaddr + STATUS_REG); 
-
 			/* Get the received packets */
 			eepro_rx(dev);
 		}
-
 		else if (status & TX_INT) {
 			if (net_debug > 4)
 				printk("eepro: packet transmit interrupt.\n");
-
 			/* Acknowledge the TX_INT */
 			outb(TX_INT, ioaddr + STATUS_REG); 
-
 			/* Process the status of transmitted packets */
 			eepro_transmit_interrupt(dev);
 		}
 	
 	} while ((boguscount-- > 0) && (status & 0x06));
-
+	
 	dev->interrupt = 0;
+		
 	if (net_debug > 5)
 		printk("eepro: exiting eepro_interrupt routine.\n");
 	
@@ -813,41 +903,39 @@
 
 	dev->tbusy = 1;
 	dev->start = 0;
-
+	
 	outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
-
+	
 	/* Disable the physical interrupt line. */
 	temp_reg = inb(ioaddr + REG1);
 	outb(temp_reg & 0x7f, ioaddr + REG1); 
-
 	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
-
+	
 	/* Flush the Tx and disable Rx. */
 	outb(STOP_RCV_CMD, ioaddr); 
+	
 	lp->tx_start = lp->tx_end = rcv_ram ;
 	lp->tx_last = 0;  
-
+	
 	/* Mask all the interrupts. */
 	outb(ALL_MASK, ioaddr + INT_MASK_REG); 
-
+	
 	/* clear all interrupts */
 	outb(ALL_MASK, ioaddr + STATUS_REG); 
-
+	
 	/* Reset the 82595 */
 	outb(RESET_CMD, ioaddr); 
-
+	
 	/* release the interrupt */
 	free_irq(dev->irq, NULL);
-
 	irq2dev_map[dev->irq] = 0;
-
+	
 	/* Update the statistics here. What statistics? */
-
 	/* We are supposed to wait for 200 us after a RESET */
 	SLOW_DOWN_IO;
 	SLOW_DOWN_IO; /* May not be enough? */
-
 	MOD_DEC_USE_COUNT;
+	
 	return 0;
 }
 
@@ -857,12 +945,12 @@
 eepro_get_stats(struct device *dev)
 {
 	struct eepro_local *lp = (struct eepro_local *)dev->priv;
-
 	return &lp->stats;
 }
 
 /* Set or clear the multicast filter for this adaptor.
  */
+
 static void
 set_multicast_list(struct device *dev)
 {
@@ -870,7 +958,7 @@
 	short ioaddr = dev->base_addr;
 	unsigned short mode;
 	struct dev_mc_list *dmi=dev->mc_list;
-
+	
 	if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63) 
 	{
 		/*
@@ -880,7 +968,6 @@
 		 *	flag is already set. If not we assert it.
 		 */
 		dev->flags|=IFF_PROMISC;		
-
 		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 		mode = inb(ioaddr + REG2);
 		outb(mode | PRMSC_Mode, ioaddr + REG2);	
@@ -889,6 +976,7 @@
 		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
 		printk("%s: promiscuous mode enabled.\n", dev->name);
 	} 
+	
 	else if (dev->mc_count==0 ) 
 	{
 		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
@@ -898,6 +986,7 @@
 		outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
 		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
 	}
+	
 	else 
 	{
 		unsigned short status, *eaddrs;
@@ -907,7 +996,6 @@
 		   corruption of the HOST_ADDRESS_REG by interrupt
 		   service routines. */
 		outb(ALL_MASK, ioaddr + INT_MASK_REG);
-
 		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
 		mode = inb(ioaddr + REG2);
 		outb(mode | Multi_IA, ioaddr + REG2);	
@@ -919,6 +1007,7 @@
 		outw(0, ioaddr + IO_PORT);
 		outw(0, ioaddr + IO_PORT);
 		outw(6*(dev->mc_count + 1), ioaddr + IO_PORT);
+	
 		for (i = 0; i < dev->mc_count; i++) 
 		{
 			eaddrs=(unsigned short *)dmi->dmi_addr;
@@ -927,15 +1016,17 @@
 			outw(*eaddrs++, ioaddr + IO_PORT);
 			outw(*eaddrs++, ioaddr + IO_PORT);
 		}
+	
 		eaddrs = (unsigned short *) dev->dev_addr;
 		outw(eaddrs[0], ioaddr + IO_PORT);
 		outw(eaddrs[1], ioaddr + IO_PORT);
 		outw(eaddrs[2], ioaddr + IO_PORT);
 		outw(lp->tx_end, ioaddr + XMT_BAR);
 		outb(MC_SETUP, ioaddr);
-
+	
 		/* Update the transmit queue */
 		i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1);
+	
 		if (lp->tx_start != lp->tx_end) 
 		{ 
 			/* update the next address and the chain bit in the 
@@ -950,15 +1041,17 @@
 		else {
 			lp->tx_start = lp->tx_end = i ;
 		}
-
+	
 		/* Acknowledge that the MC setup is done */
 		do { /* We should be doing this in the eepro_interrupt()! */
 			SLOW_DOWN_IO;
 			SLOW_DOWN_IO;
+	
 			if (inb(ioaddr + STATUS_REG) & 0x08) 
 			{
 				i = inb(ioaddr);
 				outb(0x08, ioaddr + STATUS_REG);
+	
 				if (i & 0x20) { /* command ABORTed */
 					printk("%s: multicast setup failed.\n", 
 						dev->name);
@@ -970,7 +1063,7 @@
 				}
 			}
 		} while (++boguscount < 100);
-
+	
 		/* Re-enable RX and TX interrupts */
 		outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
 	
@@ -980,8 +1073,8 @@
 
 /* The horrible routine to read a word from the serial EEPROM. */
 /* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
-
 /* The delay between EEPROM clock transitions. */
+
 #define eeprom_delay()	{ int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }}
 #define EE_READ_CMD (6 << 6)
 
@@ -1014,7 +1107,6 @@
 		retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
 		outb(ctrl_val, ee_addr);  eeprom_delay();
 	}
-
 	/* Terminate the EEPROM access. */
 	ctrl_val &= ~EECS;
 	outb(ctrl_val | EESK, ee_addr);
@@ -1035,47 +1127,47 @@
 
 	if (net_debug > 5)
 		printk("eepro: entering hardware_send_packet routine.\n");
-
+	
 	while (boguscount-- > 0) {
-
+	
 		/* Disable RX and TX interrupts.  Necessary to avoid
 	   	corruption of the HOST_ADDRESS_REG by interrupt
 	   	service routines. */
 		outb(ALL_MASK, ioaddr + INT_MASK_REG);
-
+	
 		if (dev->interrupt == 1) {  
 			/* Enable RX and TX interrupts */
 			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
 			continue;
 		}
-
+	
 		/* determine how much of the transmit buffer space is available */
 		if (lp->tx_end > lp->tx_start)
 			tx_available = XMT_RAM - (lp->tx_end - lp->tx_start);
 		else if (lp->tx_end < lp->tx_start)
 			tx_available = lp->tx_start - lp->tx_end;
 		else tx_available = XMT_RAM;
-
+	
 		if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER) 
 			>= tx_available)   /* No space available ??? */
 			{
 			eepro_transmit_interrupt(dev); /* Clean up the transmiting queue */
-
 			/* Enable RX and TX interrupts */
 			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
 			continue;
 		}
-
+	
 		last = lp->tx_end;
 		end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
-
 		if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */
+	
 			if ((RAM_SIZE - last) <= XMT_HEADER) {	
 			/* Arrrr!!!, must keep the xmt header together,
 			  several days were lost to chase this one down. */
 				last = rcv_ram;
 				end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
 			}	
+
 			else end = rcv_ram + (end - RAM_SIZE);
 		}
 
@@ -1084,19 +1176,20 @@
 		outw(0, ioaddr + IO_PORT);
 		outw(end, ioaddr + IO_PORT);
 		outw(length, ioaddr + IO_PORT);
-
+	
 		if (lp->version == LAN595)
 			outsw(ioaddr + IO_PORT, buf, (length + 3) >> 1);
+	
 		else {	/* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
 			unsigned short temp = inb(ioaddr + INT_MASK_REG);
 			outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
 			outsl(ioaddr + IO_PORT_32_BIT, buf, (length + 3) >> 2);
 			outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
 		}
-
+	
 		/* A dummy read to flush the DRAM write pipeline */
 		status = inw(ioaddr + IO_PORT); 
-
+	
 		if (lp->tx_start == lp->tx_end) {	
 			outw(last, ioaddr + XMT_BAR);
 			outb(XMT_CMD, ioaddr);
@@ -1105,34 +1198,36 @@
 		else {
 			/* update the next address and the chain bit in the 
 		   	last packet */
+
 			if (lp->tx_end != last) {
 				outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
 				outw(last, ioaddr + IO_PORT);
 			}
+	
 			outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
 			status = inw(ioaddr + IO_PORT);
 			outw(status | CHAIN_BIT, ioaddr + IO_PORT);
-
+	
 			/* Continue the transmit command */
 			outb(RESUME_XMT_CMD, ioaddr);
 		}
-
 		lp->tx_last = last;
 		lp->tx_end = end;
-
+	
 		/* Enable RX and TX interrupts */
 		outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG); 
 	
 		if (dev->tbusy) {
 			dev->tbusy = 0;
 		}
-
+	
 		if (net_debug > 5)
 			printk("eepro: exiting hardware_send_packet routine.\n");
+	
 		return;
 	}
-
 	dev->tbusy = 1;
+	
 	if (net_debug > 5)
 		printk("eepro: exiting hardware_send_packet routine.\n");
 }
@@ -1151,68 +1246,78 @@
 	
 	/* Set the read pointer to the start of the RCV */
 	outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
+	
 	rcv_event = inw(ioaddr + IO_PORT);
-
 	while (rcv_event == RCV_DONE) {
+
 		rcv_status = inw(ioaddr + IO_PORT);
 		rcv_next_frame = inw(ioaddr + IO_PORT);
 		rcv_size = inw(ioaddr + IO_PORT);
-
+	
 		if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) {
+	
 			/* Malloc up new buffer. */
 			struct sk_buff *skb;
-
 			rcv_size &= 0x3fff;
 			skb = dev_alloc_skb(rcv_size+5);
+	
 			if (skb == NULL) {
 				printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 				lp->stats.rx_dropped++;
 				break;
 			}
+	
 			skb->dev = dev;
 			skb_reserve(skb,2);
-
+	
 			if (lp->version == LAN595)
 				insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1);
+	
 			else {	/* LAN595TX or LAN595FX, capable of 32-bit I/O processing */
 				unsigned short temp = inb(ioaddr + INT_MASK_REG);
 				outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG);
 				insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), (rcv_size + 3) >> 2);
 				outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG);
 			}
-
 	
 			skb->protocol = eth_type_trans(skb,dev);	
 			netif_rx(skb);
 			lp->stats.rx_packets++;
 		}
+	
 		else { /* Not sure will ever reach here, 
 			  I set the 595 to discard bad received frames */
 			lp->stats.rx_errors++;
+	
 			if (rcv_status & 0x0100)
 				lp->stats.rx_over_errors++;
+	
 			else if (rcv_status & 0x0400)
 				lp->stats.rx_frame_errors++;
+	
 			else if (rcv_status & 0x0800)
 				lp->stats.rx_crc_errors++;
+	
 			printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n", 
 				dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size);
 		}
+	
 		if (rcv_status & 0x1000)
 			lp->stats.rx_length_errors++;
+	
 		if (--boguscount == 0)
 			break;
-
+	
 		rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
 		lp->rx_start = rcv_next_frame;
 		outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
 		rcv_event = inw(ioaddr + IO_PORT);
-
 	} 
 	if (rcv_car == 0)
 		rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff;
+	
 	outw(rcv_car - 1, ioaddr + RCV_STOP);
-
+	
 	if (net_debug > 5)
 		printk("eepro: exiting eepro_rx routine.\n");
 }
@@ -1226,17 +1331,17 @@
 	short xmt_status;
 
 	while (lp->tx_start != lp->tx_end) { 
-
+	
 		outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG);
 		xmt_status = inw(ioaddr+IO_PORT);
+	
 		if ((xmt_status & TX_DONE_BIT) == 0) break;
-
+	
 		xmt_status = inw(ioaddr+IO_PORT);
 		lp->tx_start = inw(ioaddr+IO_PORT);
-
 		dev->tbusy = 0;
 		mark_bh(NET_BH);
-
+	
 		if (xmt_status & 0x2000)
 			lp->stats.tx_packets++; 
 		else {
@@ -1246,41 +1351,45 @@
 			printk("%s: XMT status = %#x\n",
 				dev->name, xmt_status);
 		}
+	
 		if (xmt_status & 0x000f) {
 			lp->stats.collisions += (xmt_status & 0x000f);
 		}
+	
 		if ((xmt_status & 0x0040) == 0x0) {
 			lp->stats.tx_heartbeat_errors++;
 		}
-
+	
 		if (--boguscount == 0)
 			break;  
 	}
 }
 
 #ifdef MODULE
+
 static char devicename[9] = { 0, };
 static struct device dev_eepro = {
 	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
 	0, 0, 0, 0,
 	0, 0,
 	0, 0, 0, NULL, eepro_probe };
-
 static int io = 0x200;
 static int irq = 0;
 static int mem = (RCV_RAM/1024);	/* Size of the rx buffer in KB */
 
-int
+int 
 init_module(void)
 {
 	if (io == 0)
 		printk("eepro: You should not use auto-probing with insmod!\n");
+
 	dev_eepro.base_addr = io;
 	dev_eepro.irq       = irq;
 	dev_eepro.mem_end   = mem;
 
 	if (register_netdev(&dev_eepro) != 0)
 		return -EIO;
+
 	return 0;
 }
 
@@ -1288,6 +1397,7 @@
 cleanup_module(void)
 {
 	unregister_netdev(&dev_eepro);
+
 	kfree_s(dev_eepro.priv,sizeof(struct eepro_local));
 	dev_eepro.priv=NULL;
 
diff -u --recursive --new-file v2.0.35/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.0.35/linux/drivers/net/eepro100.c	Sun Nov 15 10:49:38 1998
+++ linux/drivers/net/eepro100.c	Sun Nov 15 10:33:02 1998
@@ -41,7 +41,6 @@
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
 static int multicast_filter_limit = 64;
 
-#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
@@ -182,10 +181,10 @@
 	doing the CU_RESUME
 		the chip processes the next-yet-valid post-final-command.
 So blindly sending a CU_RESUME is only safe if we do it immediately after
-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.
+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.
diff -u --recursive --new-file v2.0.35/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v2.0.35/linux/drivers/net/eexpress.c	Tue Apr  8 08:47:45 1997
+++ linux/drivers/net/eexpress.c	Sun Nov 15 10:33:03 1998
@@ -7,6 +7,11 @@
  * Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org>
  * Reworked 1995 by John Sullivan <js10039@cam.ac.uk>
  * More fixes by Philip Blundell <pjb27@cam.ac.uk>
+ * Added the Compaq LTE  Alan Cox <alan@redhat.com>
+ *
+ * Note - this driver is experimental still - it has problems on faster
+ * machines. Someone needs to sit down and go through it line by line with
+ * a databook...
  */
 
 /*
@@ -86,7 +91,8 @@
 
 static char version[] = 
 "eexpress.c: v0.10 04-May-95 John Sullivan <js10039@cam.ac.uk>\n"
-"            v0.14 19-May-96 Philip Blundell <phil@tazenda.demon.co.uk>\n";
+"            v0.14 19-May-96 Philip Blundell <phil@tazenda.demon.co.uk>\n"
+"            v0.15 04-Aug-98 Alan Cox <alan@redhat.com>\n";
 
 #include <linux/module.h>
 
@@ -701,11 +707,13 @@
 	hw_addr[1] = eexp_hw_readeeprom(ioaddr,3);
 	hw_addr[2] = eexp_hw_readeeprom(ioaddr,4);
 
-	if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000)) 
+	/* Standard Address or Compaq LTE Address */
+	if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) ||
+	      (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00)))) 
 	{
 		printk("rejected: invalid address %04x%04x%04x\n",
 			hw_addr[2],hw_addr[1],hw_addr[0]);
-		return ENODEV;
+		return -ENODEV;
 	}
 
 	dev->base_addr = ioaddr;
diff -u --recursive --new-file v2.0.35/linux/drivers/net/epic100.c linux/drivers/net/epic100.c
--- v2.0.35/linux/drivers/net/epic100.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/epic100.c	Sun Nov 15 10:33:03 1998
@@ -1,10 +1,10 @@
-/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Linux. */
+/* epic100.c: A SMC 83c170 EPIC/100 Fast Ethernet driver for Linux. */
 /*
 	Written 1997-1998 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
-    All other rights reserved.
+	All other rights reserved.
 
 	This driver is for the SMC83c170/175 "EPIC" series, as used on the
 	SMC EtherPower II 9432 PCI adapter, and several CardBus cards.
@@ -18,7 +18,7 @@
 */
 
 static const char *version =
-"epic100.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";
+"epic100.c:v1.03 8/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n";
 
 /* A few user-configurable values. */
 
@@ -43,16 +43,16 @@
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
 /* Bytes transferred to chip before transmission starts. */
-#define TX_FIFO_THRESH 128		/* Rounded down to 4 byte units. */
+#define TX_FIFO_THRESH 256		/* Rounded down to 4 byte units. */
 #define RX_FIFO_THRESH 1		/* 0-3, 0==32, 64,96, or 3==128 bytes  */
 
 #include <linux/config.h>
+#include <linux/version.h>		/* Evil, but neccessary */
 #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
@@ -68,7 +68,11 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#if LINUX_VERSION_CODE >= 0x20155
+#define PCI_SUPPORT_VER2
+#else
 #include <linux/bios32.h>
+#endif
 #include <linux/delay.h>
 
 #include <asm/processor.h>		/* Processor type for cache alignment. */
@@ -80,11 +84,15 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
-#define RUN_AT(x) (jiffies + (x))
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
 
-#if LINUX_VERSION_CODE < 0x20138
-#define test_and_set_bit(val, addr) set_bit(val, addr)
+#if ! defined (LINUX_VERSION_CODE) || LINUX_VERSION_CODE < 0x20000
+#warning This driver version is only for kernel versions 2.0.0 and later.
 #endif
+
+#define RUN_AT(x) (jiffies + (x))
+
 #if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115)
 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
 MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");
@@ -94,6 +102,18 @@
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(max_interrupt_work, "i");
 #endif
+#if LINUX_VERSION_CODE < 0x20123
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#define NETSTATS_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define DEV_FREE_SKB(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else  /* Grrr, unneeded incompatible change. */
+#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
+#endif
 
 /* The I/O extent. */
 #define EPIC_TOTAL_SIZE 0x100
@@ -106,7 +126,7 @@
 I. Board Compatibility
 
 This device driver is designed for the SMC "EPCI/100", the SMC
-single-chip ethernet controllers for PCI.  This chip is used on
+single-chip Ethernet controllers for PCI.  This chip is used on
 the SMC EtherPower II boards.
 
 
@@ -132,17 +152,31 @@
 
 */
 
-#ifndef PCI_VENDOR_ID_SMC
-#define PCI_VENDOR_ID_SMC			0x10B8
-#endif
-#ifndef PCI_DEVICE_ID_SMC_EPIC100
-#define PCI_DEVICE_ID_SMC_EPIC100	0x0005
-#endif
-
 /* The rest of these values should never change. */
+
+static struct device *epic_probe1(int pci_bus, int pci_devfn,
+									 struct device *dev, int card_idx);
+
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+struct chip_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, pci_flags;
+	int io_size, min_latency;
+	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+							 int chip_idx);
+} chip_tbl[] = {
+	{"SMSC EPIC/100", 0x10B8, 0x0005, 0x7fff,
+	 PCI_USES_IO|PCI_USES_MASTER|PCI_ADDR0, EPIC_TOTAL_SIZE, 32, epic_probe1},
+	{0,},
+};
+
 /* Offsets to registers, using the (ugh) SMC names. */
 enum epic_registers {
   COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14,
+  PCIBurstCnt=0x18,
   TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28,	/* Rx error counters. */
   MIICtrl=0x30, MIIData=0x34, MIICfg=0x38,
   LAN0=64,						/* MAC address. */
@@ -153,8 +187,9 @@
 
 /* Interrupt register bits, using my own meaningful names. */
 enum IntrStatus {
-  TxIdle=0x40000, RxIdle=0x20000,
-  CntFull=0x0200, TxUnderrun=0x0100,
+  TxIdle=0x40000, RxIdle=0x20000, IntrSummary=0x010000,
+  PCIBusErr170=0x7000, PCIBusErr175=0x1000, PhyEvent175=0x8000,
+  RxStarted=0x0800, RxEarlyWarn=0x0400, CntFull=0x0200, TxUnderrun=0x0100,
   TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010,
   RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001,
 };
@@ -167,7 +202,7 @@
 	u32 bufaddr;
 	u16 buflength;
 	u16 control;
-    u32 next;
+	u32 next;
 };
 
 struct epic_rx_desc {
@@ -175,7 +210,7 @@
 	u16 rxlength;
 	u32 bufaddr;
 	u32 buflength;
-    u32 next;
+	u32 next;
 };
 
 struct epic_private {
@@ -198,12 +233,13 @@
 	u8 pci_bus, pci_dev_fn;				/* PCI bus location. */
 	u16 chip_id;
 
-	struct enet_statistics stats;
+	struct net_device_stats stats;
 	struct timer_list timer;			/* Media selection timer. */
 	unsigned char mc_filter[8];
 	signed char phys[4];				/* MII device addresses. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
-	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int full_duplex:1;			/* Current duplex setting. */
+	unsigned int force_fd:1;			/* Full-duplex operation requested. */
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	unsigned int media2:4;				/* Secondary monitored media port. */
 	unsigned int medialock:1;			/* Don't sense media type. */
@@ -216,119 +252,140 @@
 static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 
-static struct device *epic100_probe1(int pci_bus, int pci_devfn,
-									 struct device *dev, int card_idx);
 static int epic_open(struct device *dev);
-static int read_eeprom(int ioaddr, int location);
-static int mii_read(int ioaddr, int phy_id, int location);
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(long ioaddr, int phy_id, int location);
+static void mdio_write(long ioaddr, int phy_id, int location, int value);
+static void epic_restart(struct device *dev);
 static void epic_timer(unsigned long data);
 static void epic_tx_timeout(struct device *dev);
 static void epic_init_ring(struct device *dev);
 static int epic_start_xmit(struct sk_buff *skb, struct device *dev);
 static int epic_rx(struct device *dev);
 static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
 static int epic_close(struct device *dev);
-static struct enet_statistics *epic_get_stats(struct device *dev);
+static struct net_device_stats *epic_get_stats(struct device *dev);
 static void set_rx_mode(struct device *dev);
 
 
 /* A list of all installed EPIC devices, for removing the driver module. */
 static struct device *root_epic_dev = NULL;
 
+#ifndef CARDBUS
 int epic100_probe(struct device *dev)
 {
-	static int cards_found = 0;
-	static int pci_index = 0;	/* Static, for multiple probe calls. */
-
-	/* 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
-	   Epic cards in slot order. */
-
-	if (pcibios_present()) {
-		unsigned char pci_bus, pci_device_fn;
-
-		for (;pci_index < 0xff; pci_index++) {
-			u8  pci_latency;
-			u16 pci_command, new_command, vendor, device;
-			u32 pci_ioaddr;
-
-			if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
-#ifdef REVERSE_PROBE_ORDER
-									0xff - pci_index,
+	int cards_found = 0;
+	int chip_idx;
+	u16 pci_command, new_command;
+	unsigned char pci_bus, pci_device_fn;
+
+#ifdef PCI_SUPPORT_VER2
+	struct pci_dev *pcidev = NULL;
+	while ((pcidev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pcidev))
+			!= NULL) {
+		long pci_ioaddr = pcidev->base_address[0] & ~3;
+		int vendor = pcidev->vendor;
+		int device = pcidev->device;
+
+		for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++)
+			if (vendor == chip_tbl[chip_idx].vendor_id
+				&& (device & chip_tbl[chip_idx].device_id_mask) ==
+				chip_tbl[chip_idx].device_id)
+				break;
+		if (chip_tbl[chip_idx].vendor_id == 0 		/* Compiled out! */
+			||	check_region(pci_ioaddr, chip_tbl[chip_idx].io_size))
+			continue;
+		pci_bus = pcidev->bus->number;
+		pci_device_fn = pcidev->devfn;
 #else
-									pci_index,
-#endif
-									&pci_bus, &pci_device_fn)
-				!= PCIBIOS_SUCCESSFUL)
+	int pci_index;
+
+	if ( ! pcibios_present())
+		return -ENODEV;
+
+	for (pci_index = 0; pci_index < 0xff; pci_index++) {
+		u16 vendor, device;
+		u32 pci_ioaddr;
+
+		if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
+								pci_index, &pci_bus, &pci_device_fn)
+			!= PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_VENDOR_ID, &vendor);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_DEVICE_ID, &device);
+
+		for (chip_idx = 0; chip_tbl[chip_idx].vendor_id; chip_idx++)
+			if (vendor == chip_tbl[chip_idx].vendor_id
+				&& (device & chip_tbl[chip_idx].device_id_mask) ==
+				chip_tbl[chip_idx].device_id)
 				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);
-			if (vendor != PCI_VENDOR_ID_SMC)
-				continue;
-			if (device != PCI_DEVICE_ID_SMC_EPIC100) {
-				printk("Unknown SMC PCI ethernet chip type %4.4x detected:"
-					   " not configured.\n", device);
-				continue;
-			}
+		if (chip_tbl[chip_idx].vendor_id == 0) 		/* Compiled out! */
+			continue;
 
-			/* Activate the card: fix for brain-damaged Win98 BIOSes. */
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_COMMAND, &pci_command);
-			new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
-			if (pci_command != new_command) {
-				printk(KERN_INFO "  The PCI BIOS has not enabled Ethernet"
-					   " device %4.4x-%4.4x."
-					   "  Updating PCI command %4.4x->%4.4x.\n",
-					   vendor, device, pci_command, new_command);
-				pcibios_write_config_word(pci_bus, pci_device_fn,
-										  PCI_COMMAND, new_command);
-			}
+		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 (check_region(pci_ioaddr, chip_tbl[chip_idx].io_size))
+			continue;
+#endif
+
+		/* EPIC-specific code: Soft-reset the chip ere setting as master. */
+		outl(0x0001, pci_ioaddr + GENCTL);
+
+		/* Activate the card: fix for brain-damaged Win98 BIOSes. */
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+		new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled Ethernet"
+				   " device %4.4x-%4.4x."
+				   "  Updating PCI command %4.4x->%4.4x.\n",
+				   vendor, device, pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
+
+		dev = chip_tbl[chip_idx].probe1(pci_bus, pci_device_fn,
+										dev, cards_found);
 
-			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 (check_region(pci_ioaddr, EPIC_TOTAL_SIZE))
-				continue;
-
-			dev = epic100_probe1(pci_bus, pci_device_fn, dev, cards_found);
-
-			if (dev) {
-				pcibios_read_config_byte(pci_bus, pci_device_fn,
-										 PCI_LATENCY_TIMER, &pci_latency);
-				if (pci_latency < 32) {
-					printk("  PCI latency timer (CFLT) value of %d is "
-						   "unreasonably low, setting to 32.\n", pci_latency);
-					pcibios_write_config_byte(pci_bus, pci_device_fn,
-											  PCI_LATENCY_TIMER, 32);
-				} else if (epic_debug > 1)
-					printk("  PCI latency timer (CFLT) is %#x.\n",
-						   pci_latency);
-				dev = 0;
-				cards_found++;
+		/* Check the latency timer. */
+		if (dev) {
+			u8 pci_latency;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency < chip_tbl[chip_idx].min_latency) {
+				printk(KERN_INFO "  PCI latency timer (CFLT) value of %d is "
+					   "unreasonably low, setting to %d.\n", pci_latency,
+					   chip_tbl[chip_idx].min_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER,
+										  chip_tbl[chip_idx].min_latency);
 			}
+			dev = 0;
+			cards_found++;
 		}
 	}
 
 	return cards_found ? 0 : -ENODEV;
 }
+#endif  /* not CARDBUS */
 
-static struct device *epic100_probe1(int bus, int devfn, struct device *dev,
+static struct device *epic_probe1(int bus, int devfn, struct device *dev,
 									 int card_idx)
 {
 	static int did_version = 0;			/* Already printed version info. */
-	struct epic_private *tp;
+	struct epic_private *ep;
 	int i, option = 0, duplex = 0;
-	u8 irq;
 	u16 chip_id;
 	u32 ioaddr;
 
 	if (epic_debug > 0  &&  did_version++ == 0)
-		printk(version);
+		printk(KERN_INFO "%s", version);
 
 	if (dev && dev->mem_start) {
 		option = dev->mem_start;
@@ -342,18 +399,38 @@
 
 	dev = init_etherdev(dev, 0);
 
-	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &ioaddr);
-	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
-	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &chip_id);
-	ioaddr &= ~3;
+	{		/* Grrrr, badly consider interface change. */
+#if defined(PCI_SUPPORT_VER2)
+		struct pci_dev *pdev = pci_find_slot(bus, devfn);
+		ioaddr = pdev->base_address[0] & ~3;
+		dev->irq = pdev->irq;
+		chip_id = pdev->device;
+#else
+		u8 irq;
+		u32 ioaddr0;
+		pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &ioaddr0);
+		pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
+		pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &chip_id);
+		ioaddr = ioaddr0 & ~3;
+		dev->irq = irq;
+#endif
+	}
 
-	printk("%s: SMC EPIC/100 at %#3x, IRQ %d, ", dev->name, ioaddr, irq);
+	dev->base_addr = ioaddr;
+	printk(KERN_INFO "%s: SMC EPIC/100 (chip ID %4.4x) at %#3x, IRQ %d, ",
+		   dev->name, chip_id, ioaddr, dev->irq);
 
 	/* Bring the chip out of low-power mode. */
-	outl(0x0200, ioaddr + GENCTL);
+	outl(0x4200, ioaddr + GENCTL);
 	/* Magic?!  If we don't set this bit the MII interface won't work. */
 	outl(0x0008, ioaddr + TEST1);
 
+	/* Turn on the MII transceiver. */
+	outl(0x12, ioaddr + MIICfg);
+	if (chip_id == 6)
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+	outl(0x0200, ioaddr + GENCTL);
+
 	/* This could also be read from the EEPROM. */
 	for (i = 0; i < 3; i++)
 		((u16 *)dev->dev_addr)[i] = inw(ioaddr + LAN0 + i*4);
@@ -363,57 +440,64 @@
 	printk("%2.2x.\n", dev->dev_addr[i]);
 
 	if (epic_debug > 1) {
-	  printk("%s: EEPROM contents\n", dev->name);
-	  for (i = 0; i < 64; i++)
-		printk(" %4.4x%s", read_eeprom(ioaddr, i), i % 16 == 15 ? "\n" : "");
+		printk(KERN_DEBUG "%s: EEPROM contents\n", dev->name);
+		for (i = 0; i < 64; i++)
+			printk(" %4.4x%s", read_eeprom(ioaddr, i),
+				   i % 16 == 15 ? "\n" : "");
 	}
 
 	/* We do a request_region() to register /proc/ioports info. */
 	request_region(ioaddr, EPIC_TOTAL_SIZE, "SMC EPIC/100");
 
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-
 	/* The data structures must be quadword aligned. */
-	tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
-	memset(tp, 0, sizeof(*tp));
-	dev->priv = tp;
+	ep = kmalloc(sizeof(*ep), GFP_KERNEL | GFP_DMA);
+	memset(ep, 0, sizeof(*ep));
+	dev->priv = ep;
 
-	tp->next_module = root_epic_dev;
+	ep->next_module = root_epic_dev;
 	root_epic_dev = dev;
 
-	tp->chip_id = chip_id;
+	ep->pci_bus = bus;
+	ep->pci_dev_fn = devfn;
+	ep->chip_id = chip_id;
 
 	/* Find the connected MII xcvrs.
 	   Doing this in open() would allow detecting external xcvrs later, but
 	   takes too much time. */
 	{
 		int phy, phy_idx;
-		for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
+		for (phy = 1, phy_idx = 0; phy < 32 && phy_idx < sizeof(ep->phys);
 			 phy++) {
-			int mii_status = mii_read(ioaddr, phy, 0);
+			int mii_status = mdio_read(ioaddr, phy, 1);
 			if (mii_status != 0xffff  && mii_status != 0x0000) {
-				tp->phys[phy_idx++] = phy;
-				printk("%s: MII transceiver found at address %d.\n",
-					   dev->name, phy);
+				ep->phys[phy_idx++] = phy;
+				printk(KERN_INFO "%s: MII transceiver #%d control "
+					   "%4.4x status %4.4x.\n"
+					   KERN_INFO "%s:  Autonegotiation advertising %4.4x "
+					   "link partner %4.4x.\n",
+					   dev->name, phy, mdio_read(ioaddr, phy, 0), mii_status,
+					   dev->name, mdio_read(ioaddr, phy, 4),
+					   mdio_read(ioaddr, phy, 5));
 			}
 		}
 		if (phy_idx == 0) {
-			printk("%s: ***WARNING***: No MII transceiver found!\n",
+			printk(KERN_WARNING "%s: ***WARNING***: No MII transceiver found!\n",
 				   dev->name);
 			/* Use the known PHY address of the EPII. */
-			tp->phys[0] = 3;
+			ep->phys[0] = 3;
 		}
 	}
 
-	/* Leave the chip in low-power mode. */
+	/* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */
+	if (ep->chip_id == 6)
+		outl(inl(ioaddr + NVCTL) & ~0x483C, ioaddr + NVCTL);
 	outl(0x0008, ioaddr + GENCTL);
 
 	/* The lower four bits are the media type. */
-	tp->full_duplex = duplex;
-	tp->default_port = option;
-	if (tp->default_port)
-		tp->medialock = 1;
+	ep->force_fd = duplex;
+	ep->default_port = option;
+	if (ep->default_port)
+		ep->medialock = 1;
 
 	/* The Epic-specific entries in the device structure. */
 	dev->open = &epic_open;
@@ -421,6 +505,7 @@
 	dev->stop = &epic_close;
 	dev->get_stats = &epic_get_stats;
 	dev->set_multicast_list = &set_rx_mode;
+	dev->do_ioctl = &mii_ioctl;
 
 	return dev;
 }
@@ -448,31 +533,32 @@
 
 /* The EEPROM commands include the alway-set leading bit. */
 #define EE_WRITE_CMD	(5 << 6)
-#define EE_READ_CMD		(6 << 6)
+#define EE_READ64_CMD	(6 << 6)
+#define EE_READ256_CMD	(6 << 8)
 #define EE_ERASE_CMD	(7 << 6)
 
-static int read_eeprom(int ioaddr, int location)
+static int read_eeprom(long ioaddr, int location)
 {
 	int i;
 	int retval = 0;
-	int ee_addr = ioaddr + EECTL;
-	int read_cmd = location | EE_READ_CMD;
-	
+	long ee_addr = ioaddr + EECTL;
+	int read_cmd = location |
+		(inl(ee_addr) & 0x40) ? EE_READ64_CMD : EE_READ256_CMD;
+
+	printk("EEctrl is %x.\n", inl(ee_addr));
 	outl(EE_ENB & ~EE_CS, ee_addr);
 	outl(EE_ENB, ee_addr);
-	
+
 	/* Shift the read command bits out. */
-	for (i = 10; i >= 0; i--) {
-		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+	for (i = 12; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_WRITE_1 : EE_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);
@@ -488,7 +574,7 @@
 
 #define MII_READOP		1
 #define MII_WRITEOP		2
-static int mii_read(int ioaddr, int phy_id, int location)
+static int mdio_read(long ioaddr, int phy_id, int location)
 {
 	int i;
 
@@ -496,22 +582,35 @@
 	/* Typical operation takes < 50 ticks. */
 	for (i = 4000; i > 0; i--)
 		if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0)
+			return inw(ioaddr + MIIData);
+	return 0xffff;
+}
+
+static void mdio_write(long ioaddr, int phy_id, int location, int value)
+{
+	int i;
+
+	outw(value, ioaddr + MIIData);
+	outl((phy_id << 9) | (location << 4) | MII_WRITEOP, ioaddr + MIICtrl);
+	for (i = 10000; i > 0; i--) {
+		if ((inl(ioaddr + MIICtrl) & MII_WRITEOP) == 0)
 			break;
-	return inw(ioaddr + MIIData);
+	}
+	return;
 }
 
 
 static int
 epic_open(struct device *dev)
 {
-	struct epic_private *tp = (struct epic_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 	int i;
 	int mii_reg5;
-	int full_duplex = 0;
+	ep->full_duplex = ep->force_fd;
 
 	/* Soft reset the chip. */
-	outl(0x0001, ioaddr + GENCTL);
+	outl(0x4001, ioaddr + GENCTL);
 
 	if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, "SMC EPIC/100", dev))
 		return -EAGAIN;
@@ -520,32 +619,45 @@
 
 	epic_init_ring(dev);
 
-	/* This next line by Ken Yamaguchi.. ?? */
-	outl(0x8, ioaddr + 0x1c);
+	outl(0x4000, ioaddr + GENCTL);
+	/* This next magic! line by Ken Yamaguchi.. ?? */
+	outl(0x0008, ioaddr + TEST1);
 
 	/* Pull the chip out of low-power mode, enable interrupts, and set for
-	   PCI read multiple. */
+	   PCI read multiple.  The MIIcfg setting and strange write order are
+	   required by the details of which bits are reset and the transceiver
+	   wiring on the Ositech CardBus card.
+	*/
+	outl(0x12, ioaddr + MIICfg);
+	if (ep->chip_id == 6)
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+
+#if defined(__powerpc__) || defined(__sparc__)		/* Big endian */
+	outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#else
 	outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#endif
 
 	for (i = 0; i < 3; i++)
 		outl(((u16*)dev->dev_addr)[i], ioaddr + LAN0 + i*4);
 
 	outl(TX_FIFO_THRESH, ioaddr + TxThresh);
-	full_duplex = tp->full_duplex;
 
-	mii_reg5 = mii_read(ioaddr, tp->phys[0], 5);
-	if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) {
-		full_duplex = 1;
+	mii_reg5 = mdio_read(ioaddr, ep->phys[0], 5);
+	if (mii_reg5 != 0xffff) {
+		if ((mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040)
+			ep->full_duplex = 1;
+		else if (! (mii_reg5 & 0x4000))
+			mdio_write(ioaddr, ep->phys[0], 0, 0x1200);
 		if (epic_debug > 1)
-			printk("%s: Setting %s-duplex based on MII xcvr %d"
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d"
 				   " register read of %4.4x.\n", dev->name,
-				   full_duplex ? "full" : "half", tp->phys[0],
-				   mii_read(ioaddr, tp->phys[0], 5));
+				   ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5);
 	}
 
-	outl(full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
-	outl(virt_to_bus(tp->rx_ring), ioaddr + PRxCDAR);
-	outl(virt_to_bus(tp->tx_ring), ioaddr + PTxCDAR);
+	outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+	outl(virt_to_bus(ep->rx_ring), ioaddr + PRxCDAR);
+	outl(virt_to_bus(ep->tx_ring), ioaddr + PTxCDAR);
 
 	/* Start the chip's Rx process. */
 	set_rx_mode(dev);
@@ -556,67 +668,160 @@
 	dev->start = 1;
 
 	/* Enable interrupts by setting the interrupt mask. */
-	outl(CntFull | TxUnderrun | TxDone
+	outl((ep->chip_id == 6 ? PCIBusErr175 : PCIBusErr170)
+		 | CntFull | TxUnderrun | TxDone
 		 | RxError | RxOverflow | RxFull | RxHeader | RxDone,
 		 ioaddr + INTMASK);
 
 	if (epic_debug > 1)
-		printk("%s: epic_open() ioaddr %4.4x IRQ %d status %4.4x %s-duplex.\n",
+		printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x "
+			   "%s-duplex.\n",
 			   dev->name, ioaddr, dev->irq, inl(ioaddr + GENCTL),
-			   full_duplex ? "full" : "half");
+			   ep->full_duplex ? "full" : "half");
 
 	/* 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 = &epic_timer;				/* timer handler */
-	add_timer(&tp->timer);
+	init_timer(&ep->timer);
+	ep->timer.expires = RUN_AT((24*HZ)/10);			/* 2.4 sec. */
+	ep->timer.data = (unsigned long)dev;
+	ep->timer.function = &epic_timer;				/* timer handler */
+	add_timer(&ep->timer);
 
 	return 0;
 }
 
+/* Reset the chip to recover from a PCI transaction error.
+   This may occur at interrupt time. */
+static void epic_pause(struct device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	outl(0x00000000, ioaddr + INTMASK);
+	/* Stop the chip's Tx and Rx DMA processes. */
+	outw(0x0061, ioaddr + COMMAND);
+
+	/* Update the error counts. */
+	if (inw(ioaddr + COMMAND) != 0xffff) {
+		ep->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+		ep->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+		ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+	}
+
+	/* Remove the packets on the Rx queue. */
+	epic_rx(dev);
+}
+
+static void epic_restart(struct device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+	int i;
+
+	printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n",
+		   dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx);
+	/* Soft reset the chip. */
+	outl(0x0001, ioaddr + GENCTL);
+
+	udelay(1);
+	/* Duplicate code from epic_open(). */
+	outl(0x0008, ioaddr + TEST1);
+
+#if defined(__powerpc__)		/* Big endian */
+	outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#else
+	outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#endif
+	outl(0x12, ioaddr + MIICfg);
+	if (ep->chip_id == 6)
+		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+
+	for (i = 0; i < 3; i++)
+		outl(((u16*)dev->dev_addr)[i], ioaddr + LAN0 + i*4);
+
+	outl(TX_FIFO_THRESH, ioaddr + TxThresh);
+	outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+	outl(virt_to_bus(&ep->rx_ring[ep->cur_rx%RX_RING_SIZE]), ioaddr + PRxCDAR);
+	outl(virt_to_bus(&ep->tx_ring[ep->dirty_tx%TX_RING_SIZE]),
+		 ioaddr + PTxCDAR);
+
+	/* Start the chip's Rx process. */
+	set_rx_mode(dev);
+	outl(0x000A, ioaddr + COMMAND);
+
+	/* Enable interrupts by setting the interrupt mask. */
+	outl((ep->chip_id == 6 ? PCIBusErr175 : PCIBusErr170)
+		 | CntFull | TxUnderrun | TxDone
+		 | RxError | RxOverflow | RxFull | RxHeader | RxDone,
+		 ioaddr + INTMASK);
+	printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x"
+		   " interrupt %4.4x.\n",
+			   dev->name, inl(ioaddr + COMMAND), inl(ioaddr + GENCTL),
+		   inl(ioaddr + INTSTAT));
+	return;
+}
+
 static void epic_timer(unsigned long data)
 {
 	struct device *dev = (struct device *)data;
-	struct epic_private *tp = (struct epic_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 	int next_tick = 0;
+	int mii_reg5 = mdio_read(ioaddr, ep->phys[0], 5);
 
 	if (epic_debug > 3) {
-		printk("%s: Media selection tick, Tx status %8.8x.\n",
+		printk(KERN_DEBUG "%s: Media selection tick, Tx status %8.8x.\n",
 			   dev->name, inl(ioaddr + TxSTAT));
-		printk("%s: Other registers are IntMask %4.4x IntStatus %4.4x RxStatus"
-			   " %4.4x.\n",
+		printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x "
+			   "IntStatus %4.4x RxStatus %4.4x.\n",
 			   dev->name, inl(ioaddr + INTMASK), inl(ioaddr + INTSTAT),
 			   inl(ioaddr + RxSTAT));
 	}
+	if (! ep->force_fd  &&  mii_reg5 != 0xffff) {
+		int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+		if (ep->full_duplex != duplex) {
+			ep->full_duplex = duplex;
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
+				   " partner capability of %4.4x.\n", dev->name,
+				   ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5);
+			outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+		}
+		next_tick = 60*HZ;
+	}
 
 	if (next_tick) {
-		tp->timer.expires = RUN_AT(next_tick);
-		add_timer(&tp->timer);
+		ep->timer.expires = RUN_AT(next_tick);
+		add_timer(&ep->timer);
 	}
 }
 
 static void epic_tx_timeout(struct device *dev)
 {
-	struct epic_private *tp = (struct epic_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 
 	if (epic_debug > 0) {
-		printk("%s: Transmit timeout using MII device, Tx status %4.4x.\n",
+		printk(KERN_WARNING "%s: Transmit timeout using MII device, "
+			   "Tx status %4.4x.\n",
 			   dev->name, inw(ioaddr + TxSTAT));
 		if (epic_debug > 1) {
-			printk("%s: Tx indices: dirty_tx %d, cur_tx %d.\n",
-			 dev->name, tp->dirty_tx, tp->cur_tx);
+			printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n",
+			 dev->name, ep->dirty_tx, ep->cur_tx);
 		}
 	}
+	if (inw(ioaddr + TxSTAT) & 0x10) {		/* Tx FIFO underflow. */
+		ep->stats.tx_fifo_errors++;
+		/* Restart the transmit process. */
+		outl(0x0080, ioaddr + COMMAND);
+	}
+
 	/* Perhaps stop and restart the chip's Tx processes . */
 	/* Trigger a transmit demand. */
 	outl(0x0004, dev->base_addr + COMMAND);
 
 	dev->trans_start = jiffies;
-	tp->stats.tx_errors++;
+	ep->stats.tx_errors++;
 	return;
 }
 
@@ -624,48 +829,48 @@
 static void
 epic_init_ring(struct device *dev)
 {
-	struct epic_private *tp = (struct epic_private *)dev->priv;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
 	int i;
 
-	tp->tx_full = 0;
-	tp->cur_rx = tp->cur_tx = 0;
-	tp->dirty_rx = tp->dirty_tx = 0;
+	ep->tx_full = 0;
+	ep->cur_rx = ep->cur_tx = 0;
+	ep->dirty_rx = ep->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = 0x8000;		/* Owned by Epic chip */
-		tp->rx_ring[i].buflength = PKT_BUF_SZ;
+		ep->rx_ring[i].status = 0x8000;		/* Owned by Epic chip */
+		ep->rx_ring[i].buflength = PKT_BUF_SZ;
 		{
 			/* 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;
+			ep->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 */
-			tp->rx_ring[i].bufaddr = virt_to_bus(skb->tail);
+			ep->rx_ring[i].bufaddr = virt_to_bus(skb->tail);
 		}
-		tp->rx_ring[i].next = virt_to_bus(&tp->rx_ring[i+1]);
+		ep->rx_ring[i].next = virt_to_bus(&ep->rx_ring[i+1]);
 	}
 	/* Mark the last entry as wrapping the ring. */
-	tp->rx_ring[i-1].next = virt_to_bus(&tp->rx_ring[0]);
+	ep->rx_ring[i-1].next = virt_to_bus(&ep->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 = 0x0000;
-		tp->tx_ring[i].next = virt_to_bus(&tp->tx_ring[i+1]);
+		ep->tx_skbuff[i] = 0;
+		ep->tx_ring[i].status = 0x0000;
+		ep->tx_ring[i].next = virt_to_bus(&ep->tx_ring[i+1]);
 	}
-	tp->tx_ring[i-1].next = virt_to_bus(&tp->tx_ring[0]);
+	ep->tx_ring[i-1].next = virt_to_bus(&ep->tx_ring[0]);
 }
 
 static int
 epic_start_xmit(struct sk_buff *skb, struct device *dev)
 {
-	struct epic_private *tp = (struct epic_private *)dev->priv;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
 	int entry;
 	u32 flag;
 
@@ -682,37 +887,37 @@
 	   with the "ownership" bits last. */
 
 	/* Calculate the next Tx descriptor entry. */
-	entry = tp->cur_tx % TX_RING_SIZE;
+	entry = ep->cur_tx % TX_RING_SIZE;
 
-	tp->tx_skbuff[entry] = skb;
-	tp->tx_ring[entry].txlength = (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);
-	tp->tx_ring[entry].bufaddr = virt_to_bus(skb->data);
-	tp->tx_ring[entry].buflength = skb->len;
+	ep->tx_skbuff[entry] = skb;
+	ep->tx_ring[entry].txlength = (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);
+	ep->tx_ring[entry].bufaddr = virt_to_bus(skb->data);
+	ep->tx_ring[entry].buflength = skb->len;
 
-	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
+	if (ep->cur_tx - ep->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
 	  flag = 0x10; /* No interrupt */
 	  clear_bit(0, (void*)&dev->tbusy);
-	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
+	} else if (ep->cur_tx - ep->dirty_tx == TX_RING_SIZE/2) {
 	  flag = 0x14; /* Tx-done intr. */
 	  clear_bit(0, (void*)&dev->tbusy);
-	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
+	} else if (ep->cur_tx - ep->dirty_tx < TX_RING_SIZE - 2) {
 	  flag = 0x10; /* No Tx-done intr. */
 	  clear_bit(0, (void*)&dev->tbusy);
 	} else {
 	  /* Leave room for two additional entries. */
 	  flag = 0x14; /* Tx-done intr. */
-	  tp->tx_full = 1;
+	  ep->tx_full = 1;
 	}
 
-	tp->tx_ring[entry].control = flag;
-	tp->tx_ring[entry].status = 0x8000;	/* Pass ownership to the chip. */
-	tp->cur_tx++;
+	ep->tx_ring[entry].control = flag;
+	ep->tx_ring[entry].status = 0x8000;	/* Pass ownership to the chip. */
+	ep->cur_tx++;
 	/* Trigger an immediate transmit demand. */
 	outl(0x0004, dev->base_addr + COMMAND);
 
 	dev->trans_start = jiffies;
 	if (epic_debug > 4)
-		printk("%s: Queued Tx packet size %d to slot %d, "
+		printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, "
 			   "flag %2.2x Tx status %8.8x.\n",
 			   dev->name, (int)skb->len, entry, flag,
 			   inl(dev->base_addr + TxSTAT));
@@ -725,16 +930,27 @@
 static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
 	struct device *dev = (struct device *)dev_instance;
-	struct epic_private *lp;
+	struct epic_private *ep;
 	int status, ioaddr, boguscnt = max_interrupt_work;
 
 	ioaddr = dev->base_addr;
-	lp = (struct epic_private *)dev->priv;
+	ep = (struct epic_private *)dev->priv;
+
+#if defined(__i386__)
+	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+			   dev->name);
+		dev->interrupt = 0;	/* Avoid halting machine. */
+		return;
+	}
+#else
 	if (dev->interrupt) {
-		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
 		return;
 	}
 	dev->interrupt = 1;
+#endif
 
 	do {
 		status = inl(ioaddr + INTSTAT);
@@ -745,18 +961,18 @@
 			printk("%s: interrupt  interrupt=%#8.8x new intstat=%#8.8x.\n",
 				   dev->name, status, inl(ioaddr + INTSTAT));
 
-		if ((status & (RxDone | TxEmpty | TxDone)) == 0)
+		if ((status & IntrSummary) == 0)
 			break;
 
-		if (status & RxDone)			/* Rx interrupt */
+		if (status & (RxDone | RxStarted | RxEarlyWarn))
 			epic_rx(dev);
 
 		if (status & (TxEmpty | TxDone)) {
 			int dirty_tx;
 
-			for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) {
+			for (dirty_tx = ep->dirty_tx; dirty_tx < ep->cur_tx; dirty_tx++) {
 				int entry = dirty_tx % TX_RING_SIZE;
-				int txstatus = lp->tx_ring[entry].status;
+				int txstatus = ep->tx_ring[entry].status;
 
 				if (txstatus < 0)
 					break;			/* It still hasn't been Txed */
@@ -768,66 +984,77 @@
 						printk("%s: Transmit error, Tx status %8.8x.\n",
 							   dev->name, txstatus);
 #endif
-					lp->stats.tx_errors++;
-					if (txstatus & 0x1050) lp->stats.tx_aborted_errors++;
-					if (txstatus & 0x0008) lp->stats.tx_carrier_errors++;
-					if (txstatus & 0x0040) lp->stats.tx_window_errors++;
-					if (txstatus & 0x0010) lp->stats.tx_fifo_errors++;
+					ep->stats.tx_errors++;
+					if (txstatus & 0x1050) ep->stats.tx_aborted_errors++;
+					if (txstatus & 0x0008) ep->stats.tx_carrier_errors++;
+					if (txstatus & 0x0040) ep->stats.tx_window_errors++;
+					if (txstatus & 0x0010) ep->stats.tx_fifo_errors++;
 #ifdef ETHER_STATS
-					if (txstatus & 0x1000) lp->stats.collisions16++;
+					if (txstatus & 0x1000) ep->stats.collisions16++;
 #endif
 				} else {
 #ifdef ETHER_STATS
-					if ((txstatus & 0x0002) != 0) lp->stats.tx_deferred++;
+					if ((txstatus & 0x0002) != 0) ep->stats.tx_deferred++;
 #endif
-					lp->stats.collisions += (txstatus >> 8) & 15;
-					lp->stats.tx_packets++;
+					ep->stats.collisions += (txstatus >> 8) & 15;
+					ep->stats.tx_packets++;
 				}
 
 				/* Free the original skb. */
-				dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
-				lp->tx_skbuff[entry] = 0;
+				DEV_FREE_SKB(ep->tx_skbuff[entry]);
+				ep->tx_skbuff[entry] = 0;
 			}
 
 #ifndef final_version
-			if (lp->cur_tx - dirty_tx > TX_RING_SIZE) {
+			if (ep->cur_tx - dirty_tx > TX_RING_SIZE) {
 				printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-					   dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
+					   dev->name, dirty_tx, ep->cur_tx, ep->tx_full);
 				dirty_tx += TX_RING_SIZE;
 			}
 #endif
 
-			if (lp->tx_full && dev->tbusy
-				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+			if (ep->tx_full && dev->tbusy
+				&& dirty_tx > ep->cur_tx - TX_RING_SIZE + 2) {
 				/* The ring is no longer full, clear tbusy. */
-				lp->tx_full = 0;
+				ep->tx_full = 0;
 				clear_bit(0, (void*)&dev->tbusy);
 				mark_bh(NET_BH);
 			}
 
-			lp->dirty_tx = dirty_tx;
+			ep->dirty_tx = dirty_tx;
 		}
 
 		/* Check uncommon events all at once. */
-		if (status & (CntFull | TxUnderrun | RxOverflow)) {
+		if (status & (CntFull | TxUnderrun | RxOverflow |
+					  PCIBusErr170 | PCIBusErr175)) {
+			if (status == 0xffffffff) /* Chip failed or removed (CardBus). */
+				break;
 			/* Always update the error counts to avoid overhead later. */
-			lp->stats.rx_missed_errors += inb(ioaddr + MPCNT);
-			lp->stats.rx_frame_errors += inb(ioaddr + ALICNT);
-			lp->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+			ep->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+			ep->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+			ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
 
 			if (status & TxUnderrun) { /* Tx FIFO underflow. */
-			  lp->stats.tx_fifo_errors++;
-			  /* Restart the transmit process. */
-			  outl(0x0080, ioaddr + COMMAND);
+				ep->stats.tx_fifo_errors++;
+				outl(1536, ioaddr + TxThresh);
+				/* Restart the transmit process. */
+				outl(0x0080, ioaddr + COMMAND);
 			}
 			if (status & RxOverflow) {		/* Missed a Rx frame. */
-				lp->stats.rx_errors++;
+				ep->stats.rx_errors++;
+			}
+			if (status & PCIBusErr170) {
+				printk(KERN_ERR "%s: PCI Bus Error!  EPIC status %4.4x.\n",
+					   dev->name, status);
+				epic_pause(dev);
+				epic_restart(dev);
 			}
 			/* Clear all error sources. */
 			outl(status & 0x7f18, ioaddr + INTSTAT);
 		}
 		if (--boguscnt < 0) {
-			printk("%s: Too much work at interrupt, IntrStatus=0x%8.8x.\n",
+			printk(KERN_ERR "%s: Too much work at interrupt, "
+				   "IntrStatus=0x%8.8x.\n",
 				   dev->name, status);
 			/* Clear all interrupt sources. */
 			outl(0x0001ffff, ioaddr + INTSTAT);
@@ -836,115 +1063,101 @@
 	} while (1);
 
 	if (epic_debug > 3)
-		printk("%s: exiting interrupt, intr_status=%#4.4x.\n",
+		printk(KERN_DEBUG "%s: exiting interrupt, intr_status=%#4.4x.\n",
 			   dev->name, inl(ioaddr + INTSTAT));
 
+#if defined(__i386__)
+	clear_bit(0, (void*)&dev->interrupt);
+#else
 	dev->interrupt = 0;
+#endif
 	return;
 }
 
-static int
-epic_rx(struct device *dev)
+static int epic_rx(struct device *dev)
 {
-	struct epic_private *lp = (struct epic_private *)dev->priv;
-	int entry = lp->cur_rx % RX_RING_SIZE;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+	int entry = ep->cur_rx % RX_RING_SIZE;
+	int work_done = 0;
 
 	if (epic_debug > 4)
-		printk(" In epic_rx(), entry %d %8.8x.\n", entry,
-			   lp->rx_ring[entry].status);
+		printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry,
+			   ep->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;
+	while (ep->rx_ring[entry].status >= 0  &&  ep->rx_skbuff[entry]) {
+		int status = ep->rx_ring[entry].status;
 
 		if (epic_debug > 4)
-			printk("  epic_rx() status was %8.8x.\n", status);
-		if (status & 0x2000) {
-			printk("%s: Oversized Ethernet frame spanned multiple buffers,"
-				   " status %4.4x!\n", dev->name, status);
-			  lp->stats.rx_length_errors++;
-		} else if (status & 0x0006) {
-			/* Rx Frame errors are counted in hardware. */
-			lp->stats.rx_errors++;
+			printk(KERN_DEBUG "  epic_rx() status was %8.8x.\n", status);
+		if (status & 0x2006) {
+			if (status & 0x2000) {
+				printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+					   "multiple buffers, status %4.4x!\n", dev->name, status);
+				ep->stats.rx_length_errors++;
+			} else if (status & 0x0006)
+				/* Rx Frame errors are counted in hardware. */
+				ep->stats.rx_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].rxlength - 4;
+			short pkt_len = ep->rx_ring[entry].rxlength - 4;
 			struct sk_buff *skb;
-			int rx_in_place = 0;
 
-			/* Check if the packet is long enough to just accept without
-			   copying to a properly sized skbuff. */
-			if (pkt_len > rx_copybreak) {
-				struct sk_buff *newskb;
-				char *temp;
-
-				/* Pass up the skb already on the Rx ring. */
-				skb = lp->rx_skbuff[entry];
-				temp = skb_put(skb, pkt_len);
-				if (bus_to_virt(lp->rx_ring[entry].bufaddr) != temp)
-					printk("%s: Warning -- the skbuff addresses do not match"
-						   " in epic_rx: %p vs. %p / %p.\n", dev->name,
-						   bus_to_virt(lp->rx_ring[entry].bufaddr),
-						   skb->head, temp);
-				/* Get a fresh skbuff to replace the filled one. */
-				newskb = dev_alloc_skb(PKT_BUF_SZ);
-				if (newskb) {
-					rx_in_place = 1;
-					lp->rx_skbuff[entry] = newskb;
-					newskb->dev = dev;
-					/* Align IP on 16 byte boundaries */
-					skb_reserve(newskb, 2);
-					lp->rx_ring[entry].bufaddr = virt_to_bus(newskb->tail);
-				} else			/* No memory, drop the packet. */
-				  skb = 0;
-			} else
-				skb = dev_alloc_skb(pkt_len + 2);
-			if (skb == NULL) {
-				int i;
-				printk("%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++)
-					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 = 0x8000;
-					lp->cur_rx++;
-				}
-				break;
-			}
-			skb->dev = dev;
-			if (! rx_in_place) {
-				skb_reserve(skb, 2);	/* 16 byte align the data fields */
+			/* Check if the packet is long enough to accept without copying
+			   to a minimally-sized skbuff. */
+			if (pkt_len < rx_copybreak
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	/* 16 byte align the IP header */
+#if 1 /* USE_IP_COPYSUM */
+				eth_copy_and_sum(skb, bus_to_virt(ep->rx_ring[entry].bufaddr),
+								 pkt_len, 0);
+				skb_put(skb, pkt_len);
+#else
 				memcpy(skb_put(skb, pkt_len),
-					   bus_to_virt(lp->rx_ring[entry].bufaddr), pkt_len);
+					   bus_to_virt(ep->rx_ring[entry].bufaddr), pkt_len);
+#endif
+			} else {
+				skb_put(skb = ep->rx_skbuff[entry], pkt_len);
+				ep->rx_skbuff[entry] = NULL;
 			}
 			skb->protocol = eth_type_trans(skb, dev);
 			netif_rx(skb);
-			lp->stats.rx_packets++;
+			ep->stats.rx_packets++;
 		}
-
-		lp->rx_ring[entry].status = 0x8000;
-		entry = (++lp->cur_rx) % RX_RING_SIZE;
+		work_done++;
+		entry = (++ep->cur_rx) % RX_RING_SIZE;
 	}
 
-	return 0;
+	/* Refill the Rx ring buffers. */
+	for (; ep->cur_rx - ep->dirty_rx > 0; ep->dirty_rx++) {
+		entry = ep->dirty_rx % RX_RING_SIZE;
+		if (ep->rx_skbuff[entry] == NULL) {
+			struct sk_buff *skb;
+			skb = ep->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
+			if (skb == NULL)
+				break;
+			skb->dev = dev;			/* Mark as being used by this device. */
+			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+			ep->rx_ring[entry].bufaddr = virt_to_bus(skb->tail);
+			work_done++;
+		}
+		ep->rx_ring[entry].status = 0x8000;
+	}
+	return work_done;
 }
 
-static int
-epic_close(struct device *dev)
+static int epic_close(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
-	struct epic_private *tp = (struct epic_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
 	int i;
 
 	dev->start = 0;
 	dev->tbusy = 1;
 
 	if (epic_debug > 1)
-		printk("%s: Shutting down ethercard, status was %2.2x.\n",
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
 			   dev->name, inl(ioaddr + INTSTAT));
 
 	/* Disable interrupts by clearing the interrupt mask. */
@@ -953,62 +1166,61 @@
 	outw(0x0061, ioaddr + COMMAND);
 
 	/* Update the error counts. */
-	tp->stats.rx_missed_errors += inb(ioaddr + MPCNT);
-	tp->stats.rx_frame_errors += inb(ioaddr + ALICNT);
-	tp->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+	ep->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+	ep->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+	ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
 
-	del_timer(&tp->timer);
+	del_timer(&ep->timer);
 
 	free_irq(dev->irq, dev);
 
 	/* 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 Epic chip. */
-		tp->rx_ring[i].buflength = 0;
-		tp->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */
+		struct sk_buff *skb = ep->rx_skbuff[i];
+		ep->rx_skbuff[i] = 0;
+		ep->rx_ring[i].status = 0;		/* Not owned by Epic chip. */
+		ep->rx_ring[i].buflength = 0;
+		ep->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */
 		if (skb) {
 #if LINUX_VERSION_CODE < 0x20100
 			skb->free = 1;
 #endif
-			dev_kfree_skb(skb, FREE_WRITE);
+			DEV_FREE_SKB(skb);
 		}
 	}
 	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;
+		if (ep->tx_skbuff[i])
+			DEV_FREE_SKB(ep->tx_skbuff[i]);
+		ep->tx_skbuff[i] = 0;
 	}
 
 
 	/* Green! Leave the chip in low-power mode. */
 	outl(0x0008, ioaddr + GENCTL);
-	
+
 	MOD_DEC_USE_COUNT;
 
 	return 0;
 }
 
-static struct enet_statistics *
-epic_get_stats(struct device *dev)
+static struct net_device_stats *epic_get_stats(struct device *dev)
 {
-	struct epic_private *tp = (struct epic_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
+	long ioaddr = dev->base_addr;
 
 	if (dev->start) {
 		/* Update the error counts. */
-		tp->stats.rx_missed_errors += inb(ioaddr + MPCNT);
-		tp->stats.rx_frame_errors += inb(ioaddr + ALICNT);
-		tp->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+		ep->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+		ep->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+		ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
 	}
 
-	return &tp->stats;
+	return &ep->stats;
 }
 
 /* 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
+   new frame, not around filling ep->setup_frame.  This is non-deterministic
    when re-entered but still correct. */
 
 /* The little-endian AUTODIN II ethernet CRC calculation.
@@ -1035,15 +1247,15 @@
 
 static void set_rx_mode(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
-	struct epic_private *tp = (struct epic_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	struct epic_private *ep = (struct epic_private *)dev->priv;
 	unsigned char mc_filter[8];		 /* Multicast hash filter */
 	int i;
 
 	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
 		outl(0x002C, ioaddr + RxCtrl);
 		/* Unconditionally log net taps. */
-		printk("%s: Promiscuous mode enabled.\n", dev->name);
+		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
 		memset(mc_filter, 0xff, sizeof(mc_filter));
 	} else if ((dev->mc_count > 0)  ||  (dev->flags & IFF_ALLMULTI)) {
 		/* There is apparently a chip bug, so the multicast filter
@@ -1064,14 +1276,56 @@
 					mc_filter);
 	}
 	/* ToDo: perhaps we need to stop the Tx and Rx process here? */
-	if (memcmp(mc_filter, tp->mc_filter, sizeof(mc_filter))) {
+	if (memcmp(mc_filter, ep->mc_filter, sizeof(mc_filter))) {
 		for (i = 0; i < 4; i++)
 			outw(((u16 *)mc_filter)[i], ioaddr + MC0 + i*4);
-		memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter));
+		memcpy(ep->mc_filter, mc_filter, sizeof(mc_filter));
 	}
 	return;
 }
 
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	long ioaddr = dev->base_addr;
+	u16 *data = (u16 *)&rq->ifr_data;
+
+	switch(cmd) {
+	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+		data[0] = ((struct epic_private *)dev->priv)->phys[0] & 0x1f;
+		/* Fall Through */
+	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+		if (! dev->start) {
+			outl(0x0200, ioaddr + GENCTL);
+			outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+		}
+		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+		if (! dev->start) {
+#ifdef notdef
+			outl(0x0008, ioaddr + GENCTL);
+			outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
+#endif
+		}
+		return 0;
+	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+		if (!suser())
+			return -EPERM;
+		if (! dev->start) {
+			outl(0x0200, ioaddr + GENCTL);
+			outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+		}
+		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+		if (! dev->start) {
+#ifdef notdef
+			outl(0x0008, ioaddr + GENCTL);
+			outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
+#endif
+		}
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 
 #ifdef CARDBUS
 
@@ -1091,7 +1345,7 @@
 	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
 	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
 	io &= ~3;
-	dev = epic100_probe1(bus, devfn, NULL, -1);
+	dev = epic_probe1(bus, devfn, NULL, -1);
 	if (dev) {
 		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
 		strcpy(node->dev_name, dev->name);
@@ -1103,6 +1357,33 @@
 	return NULL;
 }
 
+static void epic_suspend(dev_node_t *node)
+{
+	struct device **devp, **next;
+	printk(KERN_INFO "epic_suspend(%s)\n", node->dev_name);
+	for (devp = &root_epic_dev; *devp; devp = next) {
+		next = &((struct epic_private *)(*devp)->priv)->next_module;
+		if (strcmp((*devp)->name, node->dev_name) == 0) break;
+	}
+	if (*devp) {
+		long ioaddr = (*devp)->base_addr;
+		epic_pause(*devp);
+		/* Put the chip into low-power mode. */
+		outl(0x0008, ioaddr + GENCTL);
+	}
+}
+static void epic_resume(dev_node_t *node)
+{
+	struct device **devp, **next;
+	printk(KERN_INFO "epic_resume(%s)\n", node->dev_name);
+	for (devp = &root_epic_dev; *devp; devp = next) {
+		next = &((struct epic_private *)(*devp)->priv)->next_module;
+		if (strcmp((*devp)->name, node->dev_name) == 0) break;
+	}
+	if (*devp) {
+		epic_restart(*devp);
+	}
+}
 static void epic_detach(dev_node_t *node)
 {
 	struct device **devp, **next;
@@ -1121,7 +1402,7 @@
 }
 
 struct driver_operations epic_ops = {
-	"epic_cb", epic_attach, NULL, NULL, epic_detach
+	"epic_cb", epic_attach, epic_suspend, epic_resume, epic_detach
 };
 
 #endif  /* Cardbus support */
@@ -1169,9 +1450,8 @@
 
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c"
- *  alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c"
- *  alt-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic.c -o epic_cb.c -I/usr/src/pcmcia-cs-3.0.0/include/"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c -o epic_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
diff -u --recursive --new-file v2.0.35/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c
--- v2.0.35/linux/drivers/net/eth16i.c	Sun Nov 15 10:49:38 1998
+++ linux/drivers/net/eth16i.c	Sun Nov 15 10:33:03 1998
@@ -1,19 +1,31 @@
 /* eth16i.c An ICL EtherTeam 16i and 32 EISA ethernet driver for Linux
-
-   Written 1994-95 by Mika Kuoppala
-
-   Copyright (C) 1994, 1995 by Mika Kuoppala
-   Based on skeleton.c and at1700.c by Donald Becker
+   
+   Written 1994-1998 by Mika Kuoppala
+   
+   Copyright (C) 1994-1998 by Mika Kuoppala
+   Based on skeleton.c and heavily on at1700.c by Donald Becker
 
    This software may be used and distributed according to the terms
    of the GNU Public Licence, incorporated herein by reference.
 
-   The author may be reached as miku@elt.icl.fi
+   The author may be reached as miku@iki.fi
 
    This driver supports following cards :
 	- ICL EtherTeam 16i
-	- ICL EtherTeam 32 EISA
+	- ICL EtherTeam 32 EISA 
+	  (Uses true 32 bit transfers rather than 16i compability mode)
+
+   Example Module usage:
+        insmod eth16i.o ioaddr=0x2a0 mediatype=bnc
 
+	mediatype can be one of the following: bnc,tp,dix,auto,eprom
+
+	'auto' will try to autoprobe mediatype.
+	'eprom' will use whatever type defined in eprom.
+
+   I have benchmarked driver with PII/300Mhz as a ftp client
+   and 486/33Mhz as a ftp server. Top speed was 1128.37 kilobytes/sec.
+   
    Sources:
      - skeleton.c  a sample network driver core for linux,
        written by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
@@ -23,61 +35,109 @@
        written by Markku Viima
      - The Fujitsu MB86965 databook.
    
-   Valuable assistance from:
-	Markku Viima (ICL) 
-	Ari Valve (ICL)
-   
+   Author thanks following persons due to their valueble assistance:    
+        Markku Viima (ICL)
+	Ari Valve (ICL)      
+	Donald Becker
+	Kurt Huwig <kurt@huwig.de>
+
    Revision history:
 
    Version	Date		Description
    
-   0.01		15.12-94	Initial version (card detection)
+   0.01         15.12-94        Initial version (card detection)
    0.02         23.01-95        Interrupt is now hooked correctly
    0.03         01.02-95        Rewrote initialization part
    0.04         07.02-95        Base skeleton done...
-				Made a few changes to signature checking
-				to make it a bit reliable.
-				- fixed bug in tx_buf mapping
-				- fixed bug in initialization (DLC_EN
-				  wasn't enabled when initialization
-				  was done.)
-   0.05		08.02-95	If there were more than one packet to send,
-				transmit was jammed due to invalid
-				register write...now fixed
+                                Made a few changes to signature checking
+                                to make it a bit reliable.
+                                - fixed bug in tx_buf mapping
+                                - fixed bug in initialization (DLC_EN
+                                  wasn't enabled when initialization
+                                  was done.)
+   0.05         08.02-95        If there were more than one packet to send,
+                                transmit was jammed due to invalid
+                                register write...now fixed
    0.06         19.02-95        Rewrote interrupt handling        
    0.07         13.04-95        Wrote EEPROM read routines
                                 Card configuration now set according to
-				data read from EEPROM
+                                data read from EEPROM
    0.08         23.06-95        Wrote part that tries to probe used interface
                                 port if AUTO is selected
 
-   0.09         01.09-95	Added module support
-   
-   0.10         04.09-95	Fixed receive packet allocation to work		
-        			with kernels > 1.3.x
+   0.09         01.09-95        Added module support
    
+   0.10         04.09-95        Fixed receive packet allocation to work
+                                with kernels > 1.3.x
+      
    0.20		20.09-95	Added support for EtherTeam32 EISA	
 
    0.21         17.10-95        Removed the unnecessary extern 
 				init_etherdev() declaration. Some
 				other cleanups.
+   				
+   0.22		22.02-96	Receive buffer was not flushed
+				correctly when faulty packet was
+				received. Now fixed.
+
+   0.23		26.02-96	Made resetting the adapter	
+			 	more reliable.
+   
+   0.24		27.02-96	Rewrote faulty packet handling in eth16i_rx
+
+   0.25		22.05-96	kfree() was missing from cleanup_module.
+
+   0.26		11.06-96	Sometimes card was not found by 
+				check_signature(). Now made more reliable.
+   
+   0.27		23.06-96	Oops. 16 consecutive collisions halted 
+				adapter. Now will try to retransmit 
+				MAX_COL_16 times before finally giving up.
+   
+   0.28	        28.10-97	Added dev_id parameter (NULL) for free_irq
+
+   0.29         29.10-97        Multiple card support for module users
+
+   0.30         30.10-97        Fixed irq allocation bug.
+                                (request_irq moved from probe to open)
 
-   0.21a	15.08-97	Made signature check less restrictive to
-				detect card that have been used for booting
-				with a bootprom.
+   0.30a        21.08-98        Card detection made more relaxed. Driver
+                                had problems with some TCP/IP-PROM boots
+				to find the card. Suggested by 
 				Kurt Huwig <kurt@huwig.de>
+
+   0.31         28.08-98        Media interface port can now be selected
+                                with module parameters or kernel
+				boot parameters. 
+
+   0.32         31.08-98        IRQ was never freed if open/close 
+                                pair wasn't called. Now fixed.
+   
+   0.33         10.09-98        When eth16i_open() was called after
+                                eth16i_close() chip never recovered.
+				Now more shallow reset is made on
+				close.
+
    Bugs:
-	In some cases the interface autoprobing code doesn't find 
+	In some cases the media interface autoprobing code doesn't find 
 	the correct interface type. In this case you can 
 	manually choose the interface type in DOS with E16IC.EXE which is 
 	configuration software for EtherTeam16i and EtherTeam32 cards.
-	
+	This is also true for IRQ setting. You cannot use module
+	parameter to configure IRQ of the card (yet). 
+
    To do:
 	- Real multicast support
+	- Rewrite the media interface autoprobing code. Its _horrible_ !
+	- Possibly merge all the MB86965 specific code to external
+	  module for use by eth16.c and Donald's at1700.c
+	- IRQ configuration with module parameter. I will do
+	  this when i will get enough info about setting
+	  irq without configuration utility.
 */
 
 static char *version = 
-	"eth16i.c: v0.21a 15-08-97 Mika Kuoppala (miku@elt.icl.fi)/Kurt Huwig (kurt@huwig.de)\n";
+    "eth16i.c: v0.33 10-09-98 Mika Kuoppala (miku@iki.fi)\n";
 
 #include <linux/module.h>
 
@@ -102,22 +162,44 @@
 #include <asm/io.h>		  
 #include <asm/dma.h>
 
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+#if LINUX_VERSION_CODE < 0x20138
+#define test_and_set_bit(val,addr) set_bit(val,addr)
+#endif
+
+#if LINUX_VERSION_CODE < 0x020100
+typedef struct enet_statistics eth16i_stats_type;
+#else
+typedef struct net_device_stats eth16i_stats_type;
+#endif
+
 /* Few macros */
-#define BIT(a)		        ( (1 << (a)) )  
+#define BIT(a)		       ( (1 << (a)) )  
 #define BITSET(ioaddr, bnum)   ((outb(((inb(ioaddr)) | (bnum)), ioaddr))) 
 #define BITCLR(ioaddr, bnum)   ((outb(((inb(ioaddr)) & (~(bnum))), ioaddr)))
 
 /* This is the I/O address space for Etherteam 16i adapter. */
-#define ETH16I_IO_EXTENT 32
+#define ETH16I_IO_EXTENT       32
 
 /* Ticks before deciding that transmit has timed out */
-#define TIMEOUT_TICKS          30
+#define TX_TIMEOUT             (400*HZ/1000)
 
 /* Maximum loop count when receiving packets */
-#define MAX_RX_LOOP            40
+#define MAX_RX_LOOP            20
 
 /* Some interrupt masks */
-#define ETH16I_INTR_ON	       0x8f82
+#define ETH16I_INTR_ON	       0xef8a       /* Higher is receive mask */
 #define ETH16I_INTR_OFF	       0x0000
 	 
 /* Buffers header status byte meanings */
@@ -134,6 +216,7 @@
 #define NET_BUSY               BIT(6)
 #define TX_PKT_RCD             BIT(5)
 #define CR_LOST                BIT(4)
+#define TX_JABBER_ERR	       BIT(3)
 #define COLLISION              BIT(2)
 #define COLLISIONS_16          BIT(1)
 
@@ -186,16 +269,16 @@
 #define BUFFER_WIDTH_8         BIT(4)       /* 1 = 8bit, 0 = 16bit */
 #define TBS1                   BIT(3)       
 #define TBS0                   BIT(2)
-#define _BS1                    BIT(1)       /* 00=8kb,  01=16kb  */
-#define _BS0                    BIT(0)       /* 10=32kb, 11=64kb  */
+#define SRAM_BS1               BIT(1)       /* 00=8kb,  01=16kb  */
+#define SRAM_BS0               BIT(0)       /* 10=32kb, 11=64kb  */
 
 #ifndef ETH16I_TX_BUF_SIZE                   /* 0 = 2kb, 1 = 4kb  */ 
-#define ETH16I_TX_BUF_SIZE     2             /* 2 = 8kb, 3 = 16kb */
+#define ETH16I_TX_BUF_SIZE     3             /* 2 = 8kb, 3 = 16kb */
 #endif                                      
-#define TX_BUF_1x2048            0
-#define TX_BUF_2x2048            1
-#define TX_BUF_2x4098            2
-#define TX_BUF_2x8192            3
+#define TX_BUF_1x2048          0
+#define TX_BUF_2x2048          1
+#define TX_BUF_2x4098          2
+#define TX_BUF_2x8192          3
 
 /* Configuration Register 1 (DLCR7) */
 #define CONFIG_REG_1           7
@@ -215,15 +298,18 @@
 #define HASH_TABLE_RB          1
 
 /* Buffer memory ports */
-#define BUFFER_MEM_PORT_LB    8
-#define DATAPORT              BUFFER_MEM_PORT_LB
-#define BUFFER_MEM_PORT_HB    9
+#define BUFFER_MEM_PORT_LB     8
+#define DATAPORT               BUFFER_MEM_PORT_LB
+#define BUFFER_MEM_PORT_HB     9
 
 /* 16 Collision control register (BMPR11) */
 #define COL_16_REG             11
 #define HALT_ON_16             0x00
 #define RETRANS_AND_HALT_ON_16 0x02
 
+/* Maximum number of attempts to send after 16 concecutive collisions */
+#define MAX_COL_16	       10
+
 /* DMA Burst and Transceiver Mode Register (BMPR13) */
 #define TRANSCEIVER_MODE_REG   13
 #define TRANSCEIVER_MODE_RB    2         
@@ -235,9 +321,8 @@
 
 /* Filter Self Receive Register (BMPR14)  */
 #define FILTER_SELF_RX_REG     14
-#define SKIP_RECEIVE_PACKET    BIT(2)
+#define SKIP_RX_PACKET         BIT(2)
 #define FILTER_SELF_RECEIVE    BIT(0)
-#define RX_BUF_SKIP_PACKET     SKIP_RECEIVE_PACKET | FILTER_SELF_RECEIVE
 
 /* EEPROM Control Register (BMPR 16) */
 #define EEPROM_CTRL_REG        16
@@ -257,19 +342,20 @@
 #define EEPROM_READ            0x80
 
 /* NMC93CSx6 EEPROM Addresses */
-#define E_NODEID_0                     0x02
-#define E_NODEID_1                     0x03
-#define E_NODEID_2                     0x04
-#define E_PORT_SELECT                  0x14
-  #define E_PORT_BNC                   0
-  #define E_PORT_DIX                   1
-  #define E_PORT_TP                    2
-  #define E_PORT_AUTO                  3
-#define E_PRODUCT_CFG                  0x30
+#define E_NODEID_0             0x02
+#define E_NODEID_1             0x03
+#define E_NODEID_2             0x04
+#define E_PORT_SELECT          0x14
+  #define E_PORT_BNC           0x00
+  #define E_PORT_DIX           0x01
+  #define E_PORT_TP            0x02
+  #define E_PORT_AUTO          0x03
+  #define E_PORT_FROM_EPROM    0x04
+#define E_PRODUCT_CFG          0x30
  
 
 /* Macro to slow down io between EEPROM clock transitions */
-#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { __SLOW_DOWN_IO; }}while(0)
+#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { inb(0x80); }}while(0)
 
 /* Jumperless Configuration Register (BMPR19) */
 #define JUMPERLESS_CONFIG      19
@@ -288,14 +374,16 @@
      0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0 };
 
 /* This is the Interrupt lookup table for Eth16i card */
-static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15 };
+static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15, 0 };
+#define NUM_OF_ISA_IRQS    4
 
 /* This is the Interrupt lookup table for Eth32i card */
-static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15 };  
+static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15, 0 };  
 #define EISA_IRQ_REG	0xc89
+#define NUM_OF_EISA_IRQS   8
 
 static unsigned int eth16i_tx_buf_map[] = { 2048, 2048, 4096, 8192 };
-unsigned int boot = 1;
+static unsigned int boot = 1;
 
 /* Use 0 for production, 1 for verification, >2 for debug */
 #ifndef ETH16I_DEBUG
@@ -304,916 +392,1213 @@
 static unsigned int eth16i_debug = ETH16I_DEBUG;
 
 /* Information for each board */
+
 struct eth16i_local {
-  struct enet_statistics stats;
-  unsigned int tx_started:1;
-  unsigned char tx_queue;         /* Number of packets in transmit buffer */
-  unsigned short tx_queue_len;         
-  unsigned int tx_buf_size;
-  unsigned long open_time;
+	eth16i_stats_type stats;
+	unsigned char     tx_started;
+	unsigned char     tx_buf_busy;
+	unsigned short    tx_queue;  /* Number of packets in transmit buffer */
+	unsigned short    tx_queue_len;         
+	unsigned int      tx_buf_size;
+	unsigned long     open_time;
+	unsigned long     tx_buffered_packets;  
+	unsigned long     col_16;
 };
 
 /* Function prototypes */
 
-extern int eth16i_probe(struct device *dev);
+extern int     eth16i_probe(struct device *dev);
+
+static int     eth16i_probe1(struct device *dev, int ioaddr);
+static int     eth16i_check_signature(int ioaddr);
+static int     eth16i_probe_port(int ioaddr);
+static void    eth16i_set_port(int ioaddr, int porttype);
+static int     eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l);
+static int     eth16i_receive_probe_packet(int ioaddr);
+static int     eth16i_get_irq(int ioaddr);
+static int     eth16i_read_eeprom(int ioaddr, int offset);
+static int     eth16i_read_eeprom_word(int ioaddr);
+static void    eth16i_eeprom_cmd(int ioaddr, unsigned char command);
+static int     eth16i_open(struct device *dev);
+static int     eth16i_close(struct device *dev);
+static int     eth16i_tx(struct sk_buff *skb, struct device *dev);
+static void    eth16i_rx(struct device *dev);
+static void    eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void    eth16i_reset(struct device *dev);
+static void    eth16i_skip_packet(struct device *dev);
+static void    eth16i_multicast(struct device *dev); 
+static void    eth16i_select_regbank(unsigned char regbank, int ioaddr);
+static void    eth16i_initialize(struct device *dev);
+
+#if 0
+static int     eth16i_set_irq(struct device *dev);
+#endif
+
+#ifdef MODULE
+static ushort  eth16i_parse_mediatype(const char* s);
+#endif
 
-static int eth16i_probe1(struct device *dev, short ioaddr);
-static int eth16i_check_signature(short ioaddr);
-static int eth16i_probe_port(short ioaddr);
-static void eth16i_set_port(short ioaddr, int porttype);
-static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l);
-static int eth16i_receive_probe_packet(short ioaddr);
-static int eth16i_get_irq(short ioaddr);
-static int eth16i_read_eeprom(int ioaddr, int offset);
-static int eth16i_read_eeprom_word(int ioaddr);
-static void eth16i_eeprom_cmd(int ioaddr, unsigned char command);
-static int eth16i_open(struct device *dev);
-static int eth16i_close(struct device *dev);
-static int eth16i_tx(struct sk_buff *skb, struct device *dev);
-static void eth16i_rx(struct device *dev);
-static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void eth16i_multicast(struct device *dev); 
-static void eth16i_select_regbank(unsigned char regbank, short ioaddr);
-static void eth16i_initialize(struct device *dev);
 static struct enet_statistics *eth16i_get_stats(struct device *dev);
 
 static char *cardname = "ICL EtherTeam 16i/32";
 
 #ifdef HAVE_DEVLIST 
+
 /* Support for alternate probe manager */
 /struct netdev_entry eth16i_drv = 
-   {"eth16i", eth16i_probe1, ETH16I_IO_EXTENT, eth16i_probe_list}; 
+ {"eth16i", eth16i_probe1, ETH16I_IO_EXTENT, eth16i_probe_list}; 
 
 #else  /* Not HAVE_DEVLIST */
-int eth16i_probe(struct device *dev)
+
+__initfunc(int eth16i_probe(struct device *dev))
 {
-  int i;
-  int ioaddr;
-  int base_addr = dev ? dev->base_addr : 0;
-
-  if(eth16i_debug > 4) 
-    printk("Probing started for %s\n", cardname);
-
-  if(base_addr > 0x1ff)           /* Check only single location */
-    return eth16i_probe1(dev, base_addr);
-  else if(base_addr != 0)         /* Don't probe at all */
-    return ENXIO;
-
-  /* Seek card from the ISA io address space */
-  for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) {
-    if(check_region(ioaddr, ETH16I_IO_EXTENT))
-      continue;
-    if(eth16i_probe1(dev, ioaddr) == 0)
-      return 0;
-  }
-
-  /* Seek card from the EISA io address space */
-  for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++) {
-    if(check_region(ioaddr, ETH16I_IO_EXTENT))
-	continue;
-    if(eth16i_probe1(dev, ioaddr) == 0)
-	return 0;
-   }
+	int i;
+	int ioaddr;
+	int base_addr = dev ? dev->base_addr : 0;
+    
+	if(eth16i_debug > 4) 
+		printk(KERN_DEBUG "Probing started for %s\n", cardname);
+
+	if(base_addr > 0x1ff)           /* Check only single location */
+		return eth16i_probe1(dev, base_addr);
+	else if(base_addr != 0)         /* Don't probe at all */
+		return ENXIO;
+
+	/* Seek card from the ISA io address space */
+	for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) {
+		if(check_region(ioaddr, ETH16I_IO_EXTENT))
+			continue;
+		if(eth16i_probe1(dev, ioaddr) == 0)
+			return 0;
+	}
+
+	/* Seek card from the EISA io address space */
+	for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++) {
+		if(check_region(ioaddr, ETH16I_IO_EXTENT))
+			continue;
+		if(eth16i_probe1(dev, ioaddr) == 0)
+			return 0;
+	}
 
-  return ENODEV;
+	return ENODEV;
 }
 #endif  /* Not HAVE_DEVLIST */
 
-static int eth16i_probe1(struct device *dev, short ioaddr)
+__initfunc(static int eth16i_probe1(struct device *dev, int ioaddr))
 {
-  static unsigned version_printed = 0;
-  unsigned int irq = 0;
-  boot = 1;          /* To inform initialization that we are in boot probe */
-
-  /*
-     The MB86985 chip has on register which holds information in which 
-     io address the chip lies. First read this register and compare
-     it to our current io address and if match then this could
-     be our chip.
-  */
-  
-  if(ioaddr < 0x1000) {
-    if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)] != ioaddr)
-      return -ENODEV;
-  }
+	static unsigned version_printed = 0;
+	boot = 1;  /* To inform initilization that we are in boot probe */
 
-  /* Now we will go a bit deeper and try to find the chip's signature */
+	/*
+	  The MB86985 chip has on register which holds information in which 
+	  io address the chip lies. First read this register and compare
+	  it to our current io address and if match then this could
+	  be our chip.
+	  */
 
-  if(eth16i_check_signature(ioaddr) != 0) /* Can we find the signature here */
-    return -ENODEV;
-  
-  /* 
-     Now it seems that we have found a ethernet chip in this particular
-     ioaddr. The MB86985 chip has this feature, that when you read a 
-     certain register it will increase its io base address to next
-     configurable slot. Now when we have found the chip, first thing is
-     to make sure that the chip's ioaddr will hold still here.
-  */
-  
-  eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
-  outb(0x00, ioaddr + TRANSCEIVER_MODE_REG);
-  
-  outb(0x00, ioaddr + RESET);             /* Will reset some parts of chip */
-  BITSET(ioaddr + CONFIG_REG_0, BIT(7));  /* This will disable the data link */
-  
-  if(dev == NULL)
-    dev = init_etherdev(0, sizeof(struct eth16i_local));
+	if(ioaddr < 0x1000) {
+	
+		if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)] 
+		   != ioaddr)
+			return -ENODEV;
+	}
 
-  if( (eth16i_debug & version_printed++) == 0)
-    printk(version);
+	/* Now we will go a bit deeper and try to find the chip's signature */
 
-  dev->base_addr = ioaddr;
- 
-  irq = eth16i_get_irq(ioaddr);
-  dev->irq = irq;
+	if(eth16i_check_signature(ioaddr) != 0) 
+		return -ENODEV;
 
-  /* Try to obtain interrupt vector */
-  if(request_irq(dev->irq, &eth16i_interrupt, 0, "eth16i", NULL)) {
-    printk("%s: %s at %#3x, but is unusable due 
-           conflict on IRQ %d.\n", dev->name, cardname, ioaddr, irq);
-    return EAGAIN;
-  }
-
-  printk("%s: %s at %#3x, IRQ %d, ", 
-	 dev->name, cardname, ioaddr, dev->irq);
-
-  /* Let's grab the region */
-  request_region(ioaddr, ETH16I_IO_EXTENT, "eth16i");
-
-  /* Now we will have to lock the chip's io address */
-  eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
-  outb(0x38, ioaddr + TRANSCEIVER_MODE_REG); 
+	/* 
+	   Now it seems that we have found a ethernet chip in this particular
+	   ioaddr. The MB86985 chip has this feature, that when you read a 
+	   certain register it will increase it's io base address to next
+	   configurable slot. Now when we have found the chip, first thing is
+	   to make sure that the chip's ioaddr will hold still here.
+	   */
+
+	eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+	outb(0x00, ioaddr + TRANSCEIVER_MODE_REG);
+
+	outb(0x00, ioaddr + RESET);             /* Reset some parts of chip */
+	BITSET(ioaddr + CONFIG_REG_0, BIT(7));  /* Disable the data link */
 
-  eth16i_initialize(dev);   /* Initialize rest of the chip's registers */
-  
-  /* Now let's same some energy by shutting down the chip ;) */
-  BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
-   
-  /* Initialize the device structure */
-  if(dev->priv == NULL)
-    dev->priv = kmalloc(sizeof(struct eth16i_local), GFP_KERNEL);
-  memset(dev->priv, 0, sizeof(struct eth16i_local));
-
-  dev->open               = eth16i_open;
-  dev->stop               = eth16i_close;
-  dev->hard_start_xmit    = eth16i_tx;
-  dev->get_stats          = eth16i_get_stats;
-  dev->set_multicast_list = eth16i_multicast;
+	if(dev == NULL)
+		dev = init_etherdev(0, 0);
+
+	if( (eth16i_debug & version_printed++) == 0)
+		printk(KERN_INFO "%s", version);
 
-  /* Fill in the fields of the device structure with ethernet values. */
-  ether_setup(dev);
+	dev->base_addr = ioaddr;
+	
+#if 0
+	if(dev->irq) {
+		if(eth16i_set_irq(dev)) {
+			dev->irq = eth16i_get_irq(ioaddr);
+		}
 
-  boot = 0;
+	}
+	else {
+#endif
 
-  return 0;
+	dev->irq = eth16i_get_irq(ioaddr);
+
+	/* Try to obtain interrupt vector */
+
+	if (request_irq(dev->irq, (void *)&eth16i_interrupt, 0, "eth16i", dev)) {	
+		printk(KERN_WARNING "%s: %s at %#3x, but is unusable due conflicting IRQ %d.\n", 
+		       dev->name, cardname, ioaddr, dev->irq);
+		return -EAGAIN;
+	}
+
+#if 0
+	irq2dev_map[dev->irq] = dev;
+#endif
+
+	printk(KERN_INFO "%s: %s at %#3x, IRQ %d, ",
+	       dev->name, cardname, ioaddr, dev->irq);
+
+	/* Let's grab the region */
+	request_region(ioaddr, ETH16I_IO_EXTENT, "eth16i");
+
+	/* Now we will have to lock the chip's io address */
+	eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+	outb(0x38, ioaddr + TRANSCEIVER_MODE_REG); 
+
+	eth16i_initialize(dev);   /* Initialize rest of the chip's registers */
+
+	/* Now let's same some energy by shutting down the chip ;) */
+	BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
+
+	/* Initialize the device structure */
+	if(dev->priv == NULL) {
+		dev->priv = kmalloc(sizeof(struct eth16i_local), GFP_KERNEL);
+		if(dev->priv == NULL)
+			return -ENOMEM;
+	}
+
+	memset(dev->priv, 0, sizeof(struct eth16i_local));
+
+	dev->open               = eth16i_open;
+	dev->stop               = eth16i_close;
+	dev->hard_start_xmit    = eth16i_tx;
+	dev->get_stats          = eth16i_get_stats;
+	dev->set_multicast_list = &eth16i_multicast;
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	boot = 0;
+
+	return 0;
 }
 
 
 static void eth16i_initialize(struct device *dev)
 {
-  short ioaddr = dev->base_addr;
-  int i, node_w = 0;
-  unsigned char node_byte = 0;
-
-  /* Setup station address */
-  eth16i_select_regbank(NODE_ID_RB, ioaddr);
-  for(i = 0 ; i < 3 ; i++) {
-    unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i);
-    ((unsigned short *)dev->dev_addr)[i] = ntohs(node_val);
-  }
-
-  for(i = 0; i < 6; i++) { 
-    outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i);
-    if(boot) {
-      printk("%02x", inb(ioaddr + NODE_ID_0 + i));
-      if(i != 5)
-	printk(":");
-    }
-  }
-
-  /* Now we will set multicast addresses to accept none */
-  eth16i_select_regbank(HASH_TABLE_RB, ioaddr);
-  for(i = 0; i < 8; i++) 
-    outb(0x00, ioaddr + HASH_TABLE_0 + i);
-
-  /*
-     Now let's disable the transmitter and receiver, set the buffer ram 
-     cycle time, bus width and buffer data path width. Also we shall
-     set transmit buffer size and total buffer size.
-  */
-
-  eth16i_select_regbank(2, ioaddr);
-
-  node_byte = 0;
-  node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG);
+	int ioaddr = dev->base_addr;
+	int i, node_w = 0;
+	unsigned char node_byte = 0;
+
+	/* Setup station address */
+	eth16i_select_regbank(NODE_ID_RB, ioaddr);
+	for(i = 0 ; i < 3 ; i++) {
+		unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i);
+		((unsigned short *)dev->dev_addr)[i] = ntohs(node_val);
+	}
 
-  if( (node_w & 0xFF00) == 0x0800)
-    node_byte |= BUFFER_WIDTH_8;
+	for(i = 0; i < 6; i++) { 
+		outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i);
+		if(boot) {
+			printk("%02x", inb(ioaddr + NODE_ID_0 + i));
+			if(i != 5)
+				printk(":");
+		}
+	}
 
-  node_byte |= _BS1;
+	/* Now we will set multicast addresses to accept none */
+	eth16i_select_regbank(HASH_TABLE_RB, ioaddr);
+	for(i = 0; i < 8; i++) 
+		outb(0x00, ioaddr + HASH_TABLE_0 + i);
 
-  if( (node_w & 0x00FF) == 64)
-    node_byte |= _BS0;
-  
-  node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2);
+	/*
+	  Now let's disable the transmitter and receiver, set the buffer ram 
+	  cycle time, bus width and buffer data path width. Also we shall
+	  set transmit buffer size and total buffer size.
+	  */
 
-  outb(node_byte, ioaddr + CONFIG_REG_0);
+	eth16i_select_regbank(2, ioaddr);
 
-  /* We shall halt the transmitting, if 16 collisions are detected */
-  outb(RETRANS_AND_HALT_ON_16, ioaddr + COL_16_REG);
+	node_byte = 0;
+	node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG);
 
-  if(boot) /* Now set port type */
-  {
-    char *porttype[] = {"BNC", "DIX", "TP", "AUTO"};
-    
-    ushort ptype = eth16i_read_eeprom(ioaddr, E_PORT_SELECT);
-    dev->if_port = (ptype & 0x00FF);
-    
-    printk(" %s interface.\n", porttype[dev->if_port]);
+	if( (node_w & 0xFF00) == 0x0800)
+		node_byte |= BUFFER_WIDTH_8;
 
-    if(ptype == E_PORT_AUTO)
-      ptype = eth16i_probe_port(ioaddr);
-    
-    eth16i_set_port(ioaddr, ptype);
-  }
+	node_byte |= SRAM_BS1;
+
+	if( (node_w & 0x00FF) == 64)
+		node_byte |= SRAM_BS0;
+
+	node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2);
+
+	outb(node_byte, ioaddr + CONFIG_REG_0);
+
+	/* We shall halt the transmitting, if 16 collisions are detected */
+	outb(HALT_ON_16, ioaddr + COL_16_REG);
+
+#ifdef MODULE
+	/* if_port already set by init_module() */
+#else
+	dev->if_port = (dev->mem_start < E_PORT_FROM_EPROM) ? 
+		dev->mem_start : E_PORT_FROM_EPROM;
+#endif
+
+	/* Set interface port type */
+	if(boot) {
+		char *porttype[] = {"BNC", "DIX", "TP", "AUTO", "FROM_EPROM" };
+
+		switch(dev->if_port)
+		{
+
+		case E_PORT_FROM_EPROM:
+			dev->if_port = eth16i_read_eeprom(ioaddr, E_PORT_SELECT);
+			break;
+
+		case E_PORT_AUTO:
+			dev->if_port = eth16i_probe_port(ioaddr);
+			break;
+			
+		case E_PORT_BNC:
+		case E_PORT_TP:
+		case E_PORT_DIX:
+			break;
+		}
+
+		printk(" %s interface.\n", porttype[dev->if_port]);
+
+		eth16i_set_port(ioaddr, dev->if_port);
+	}
 
-  /* Set Receive Mode to normal operation */
-  outb(MODE_2, ioaddr + RECEIVE_MODE_REG);
+	/* Set Receive Mode to normal operation */
+	outb(MODE_2, ioaddr + RECEIVE_MODE_REG);
 }
 
-static int eth16i_probe_port(short ioaddr)
+static int eth16i_probe_port(int ioaddr)
 {
-  int i;
-  int retcode;
-  unsigned char dummy_packet[64] = { 0 };
+	int i;
+	int retcode;
+	unsigned char dummy_packet[64] = { 0 };
 
-  /* Powerup the chip */
-  outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
+	/* Powerup the chip */
+	outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
 
-  BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+	BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
 
-  eth16i_select_regbank(NODE_ID_RB, ioaddr);
+	eth16i_select_regbank(NODE_ID_RB, ioaddr);
 
-  for(i = 0; i < 6; i++) {
-    dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i);
-    dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i);
-  }
+	for(i = 0; i < 6; i++) {
+		dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i);
+		dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i);
+	}
 
-  dummy_packet[12] = 0x00;
-  dummy_packet[13] = 0x04;
+	dummy_packet[12] = 0x00;
+	dummy_packet[13] = 0x04;
 
-  eth16i_select_regbank(2, ioaddr);
+	eth16i_select_regbank(2, ioaddr);
 
-  for(i = 0; i < 3; i++) {
-    BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
-    BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
-    eth16i_set_port(ioaddr, i);
-   
-    if(eth16i_debug > 1)
-    	printk("Set port number %d\n", i);
+	for(i = 0; i < 3; i++) {
+		BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+		BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+		eth16i_set_port(ioaddr, i);
+
+		if(eth16i_debug > 1)
+			printk(KERN_DEBUG "Set port number %d\n", i);
+
+		retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64);
+		if(retcode == 0) {
+			retcode = eth16i_receive_probe_packet(ioaddr);
+			if(retcode != -1) {
+				if(eth16i_debug > 1)
+					printk(KERN_DEBUG "Eth16i interface port found at %d\n", i);
+				return i;
+			}
+		}
+		else {
+			if(eth16i_debug > 1)
+				printk(KERN_DEBUG "TRANSMIT_DONE timeout when probing interface port\n");
+		}
+	}
 
-    retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64);
-    if(retcode == 0) {
-      retcode = eth16i_receive_probe_packet(ioaddr);
-      if(retcode != -1) {
-	if(eth16i_debug > 1)
-		printk("Eth16i interface port found at %d\n", i);
-	return i;
-      }
-    }
-    else {
-      if(eth16i_debug > 1)
-      	printk("TRANSMIT_DONE timeout\n");
-    }
-  }
-  
-  if( eth16i_debug > 1)
-  	printk("Using default port\n");
- 
- return E_PORT_BNC;
+	if( eth16i_debug > 1)
+		printk(KERN_DEBUG "Using default port\n");
+
+	return E_PORT_BNC;
 }
 
-static void eth16i_set_port(short ioaddr, int porttype)
+static void eth16i_set_port(int ioaddr, int porttype)
 { 
-    unsigned short temp = 0;
+	unsigned short temp = 0;
 
-    eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
-    outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG);
+	eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+	outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG);
 
-    temp |= DIS_AUTO_PORT_SEL;
+	temp |= DIS_AUTO_PORT_SEL;
 
-    switch(porttype) {
+	switch(porttype) {
 
-    case E_PORT_BNC :
-      temp |= AUI_SELECT;
-      break;
+	case E_PORT_BNC :
+		temp |= AUI_SELECT;
+		break;
 
-    case E_PORT_TP :
-      break;
-      
-    case E_PORT_DIX :
-      temp |= AUI_SELECT;
-      BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT);
-      break;
-    }  
-    outb(temp, ioaddr + TRANSCEIVER_MODE_REG);
+	case E_PORT_TP :
+		break;
+
+	case E_PORT_DIX :
+		temp |= AUI_SELECT;
+		BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT);
+		break;
+	}  
+
+	outb(temp, ioaddr + TRANSCEIVER_MODE_REG);
 
-    if(eth16i_debug > 1) {
-    	printk("TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG));
-    	printk("TRANSCEIVER_MODE_REG = %x\n", inb(ioaddr+TRANSCEIVER_MODE_REG));
-    }
+	if(eth16i_debug > 1) {
+		printk(KERN_DEBUG "TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG));
+		printk(KERN_DEBUG "TRANSCEIVER_MODE_REG = %x\n", 
+		       inb(ioaddr+TRANSCEIVER_MODE_REG));
+	}
 }
 
-static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l)
+static int eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l)
 {
-  int starttime;
+	int starttime;
 
-  outb(0xff, ioaddr + TX_STATUS_REG);
+	outb(0xff, ioaddr + TX_STATUS_REG);
 
-  outw(l, ioaddr + DATAPORT);
-  outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1);  
-  
-  starttime = jiffies;
-  outb(TX_START | 1, ioaddr + TRANSMIT_START_REG); 
+	outw(l, ioaddr + DATAPORT);
+	outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1);  
 
-  while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) {
-    if( (jiffies - starttime) > TIMEOUT_TICKS) {
-      break;
-    }
-  }
-  
-  return(0);
+	starttime = jiffies;
+	outb(TX_START | 1, ioaddr + TRANSMIT_START_REG); 
+
+	while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) {
+		if( (jiffies - starttime) > TX_TIMEOUT) {
+			return -1;
+		}
+	}
+
+	return 0;
 }
 
-static int eth16i_receive_probe_packet(short ioaddr)
+static int eth16i_receive_probe_packet(int ioaddr)
 {
-  int starttime;
-  
-  starttime = jiffies;
-  
-  while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) {
-    if( (jiffies - starttime) > TIMEOUT_TICKS) {
-      
-      if(eth16i_debug > 1)
-	printk("Timeout occurred waiting transmit packet received\n");
-      starttime = jiffies;
-      while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) {
-	if( (jiffies - starttime) > TIMEOUT_TICKS) {
-	if(eth16i_debug > 1)
-	  printk("Timeout occurred waiting receive packet\n");
-        return -1;
-        }
-      }
-      
-      if(eth16i_debug > 1)
-      	printk("RECEIVE_PACKET\n");
-      return(0); /* Found receive packet */
-    }
-  }
+	int starttime;
+
+	starttime = jiffies;
 
-  if(eth16i_debug > 1) {
-  	printk("TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG));
-  	printk("RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG));
-  }
+	while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) {
+		if( (jiffies - starttime) > TX_TIMEOUT) {
+
+			if(eth16i_debug > 1)
+				printk(KERN_DEBUG "Timeout occured waiting transmit packet received\n");
+			starttime = jiffies;
+			while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) {
+				if( (jiffies - starttime) > TX_TIMEOUT) {
+					if(eth16i_debug > 1)
+						printk(KERN_DEBUG "Timeout occured waiting receive packet\n");
+					return -1;
+				}
+			}
+
+			if(eth16i_debug > 1)
+				printk(KERN_DEBUG "RECEIVE_PACKET\n");
+			return(0); /* Found receive packet */
+		}
+	}
+
+	if(eth16i_debug > 1) {
+		printk(KERN_DEBUG "TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG));
+		printk(KERN_DEBUG "RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG));
+	}
 
-  return(0); /* Return success */
+	return(0); /* Return success */
 }
 
-static int eth16i_get_irq(short ioaddr)
+#if 0
+static int eth16i_set_irq(struct device* dev)
 {
-  unsigned char cbyte;
-  
-  if( ioaddr < 0x1000) {
-  	cbyte = inb(ioaddr + JUMPERLESS_CONFIG);
-	return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] );
-  } else {  /* Oh..the card is EISA so method getting IRQ different */
-	unsigned short index = 0;
-	cbyte = inb(ioaddr + EISA_IRQ_REG);
-	while( (cbyte & 0x01) == 0) {
-		cbyte = cbyte >> 1;
-		index++;
-        }
-	return( eth32i_irqmap[ index ] );
-  }
+	const int ioaddr = dev->base_addr;
+	const int irq = dev->irq;
+	int i = 0;
+
+	if(ioaddr < 0x1000) {		
+		while(eth16i_irqmap[i] && eth16i_irqmap[i] != irq)
+			i++;
+	
+		if(i < NUM_OF_ISA_IRQS) {
+			u8 cbyte = inb(ioaddr + JUMPERLESS_CONFIG);
+			cbyte = (cbyte & 0x3F) | (i << 6);
+			outb(cbyte, ioaddr + JUMPERLESS_CONFIG);
+			return 0;
+		}
+	}
+	else {
+		printk(KERN_NOTICE "%s: EISA Interrupt cannot be set. Use EISA Configuration utility.\n", dev->name);
+	}
+	
+	return -1;
+
 }
+#endif
 
-static int eth16i_check_signature(short ioaddr)
+static int eth16i_get_irq(int ioaddr)
 {
-  int i;
-  unsigned char creg[4] = { 0 };
-  
-  for(i = 0; i < 4 ; i++) {
+	unsigned char cbyte;
 
-    creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i);
-  
-    if(eth16i_debug > 1)
-	printk("eth16i: read signature byte %x at %x\n", creg[i],
-	       ioaddr + TRANSMIT_MODE_REG + i);
-  }
+	if( ioaddr < 0x1000) {
+		cbyte = inb(ioaddr + JUMPERLESS_CONFIG);
+		return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] );
+	} else {  /* Oh..the card is EISA so method getting IRQ different */
+		unsigned short index = 0;
+		cbyte = inb(ioaddr + EISA_IRQ_REG);
+		while( (cbyte & 0x01) == 0) {
+			cbyte = cbyte >> 1;
+			index++;
+		}
+		return( eth32i_irqmap[ index ] );
+	}
+}
 
-  creg[0] &= 0x0F;      /* Mask collision cnr */
-  creg[2] &= 0x7F;      /* Mask DCLEN bit */
+static int eth16i_check_signature(int ioaddr)
+{
+	int i;
+	unsigned char creg[4] = { 0 };
 
-#if 0
-/* 
-	This was removed because the card was sometimes left to state
-  	from which it couldn't be find anymore. If there is need
-	to more strict chech still this have to be fixed.
-*/
-  if( !( (creg[0] == 0x06) && (creg[1] == 0x41)) ) {
-    if(creg[1] != 0x42)
-      return -1;
-  }
-#endif
+	for(i = 0; i < 4 ; i++) {
 
-  if( !( (creg[2] == 0x36) && (creg[3] == 0xE0)) ) {
-      creg[2] &= 0x40;
-      creg[3] &= 0x03;
+		creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i);
 
-      if( !( (creg[2] == 0x40) && (creg[3] == 0x00)) )
-	return -1;
-  }
+		if(eth16i_debug > 1)
+			printk("eth16i: read signature byte %x at %x\n", 
+			       creg[i],
+			       ioaddr + TRANSMIT_MODE_REG + i);
+	}
 
-  if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0)
-    return -1;
-  if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00)
-    return -1;
+	creg[0] &= 0x0F;      /* Mask collision cnr */
+	creg[2] &= 0x7F;      /* Mask DCLEN bit */
+
+#ifdef 0
+	/* 
+	   This was removed because the card was sometimes left to state
+	   from which it couldn't be find anymore. If there is need
+	   to more strict check still this have to be fixed.
+	   */
+	if( ! ((creg[0] == 0x06) && (creg[1] == 0x41)) ) {
+		if(creg[1] != 0x42)
+			return -1;
+	}
+#endif
 
-  return 0;
+	if( !((creg[2] == 0x36) && (creg[3] == 0xE0)) ) {
+		creg[2] &= 0x40;
+		creg[3] &= 0x03;
+		
+		if( !((creg[2] == 0x40) && (creg[3] == 0x00)) )
+			return -1;
+	}
+	
+	if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0)
+		return -1;
+	
+	if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00)
+		return -1;
+
+	return 0;
 }
 
 static int eth16i_read_eeprom(int ioaddr, int offset)
 {
-  int data = 0;
+	int data = 0;
 
-  eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset);
-  outb(CS_1, ioaddr + EEPROM_CTRL_REG);
-  data = eth16i_read_eeprom_word(ioaddr);
-  outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
+	eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset);
+	outb(CS_1, ioaddr + EEPROM_CTRL_REG);
+	data = eth16i_read_eeprom_word(ioaddr);
+	outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
 
-  return(data);  
+	return(data);  
 }
 
 static int eth16i_read_eeprom_word(int ioaddr)
 {
-  int i;
-  int data = 0;
-
-  for(i = 16; i > 0; i--) {
-    outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
-    eeprom_slow_io();
-    outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
-    eeprom_slow_io();
-    data = (data << 1) | ((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0);
-    eeprom_slow_io();
-  }
+	int i;
+	int data = 0;
+     
+	for(i = 16; i > 0; i--) {
+		outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+		eeprom_slow_io();
+		outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+		eeprom_slow_io();
+		data = (data << 1) | 
+			((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0);
+		
+		eeprom_slow_io();
+	}
 
-  return(data);
+	return(data);
 }
 
 static void eth16i_eeprom_cmd(int ioaddr, unsigned char command)
 {
-  int i;
-  
-  outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
-  outb(DI_0, ioaddr + EEPROM_DATA_REG);
-  outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
-  outb(DI_1, ioaddr + EEPROM_DATA_REG);
-  outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
-
-  for(i = 7; i >= 0; i--) {
-    short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 );
-    outb(cmd, ioaddr + EEPROM_DATA_REG);
-    outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
-    eeprom_slow_io();
-    outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
-    eeprom_slow_io();
-  } 
+	int i;
+
+	outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
+	outb(DI_0, ioaddr + EEPROM_DATA_REG);
+	outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+	outb(DI_1, ioaddr + EEPROM_DATA_REG);
+	outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+
+	for(i = 7; i >= 0; i--) {
+		short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 );
+		outb(cmd, ioaddr + EEPROM_DATA_REG);
+		outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+		eeprom_slow_io();
+		outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+		eeprom_slow_io();
+	} 
 }
 
 static int eth16i_open(struct device *dev)
 {
-  struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
-  int ioaddr = dev->base_addr;
-  
-  irq2dev_map[dev->irq] = dev;
+	struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	
+	/* Powerup the chip */
+	outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
 
-  /* Powerup the chip */
-  outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
-  
-  /* Initialize the chip */
-  eth16i_initialize(dev);  
-  
-  /* Set the transmit buffer size */
-  lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03];
-  
-  if(eth16i_debug > 3)
-    printk("%s: transmit buffer size %d\n", dev->name, lp->tx_buf_size);
+	/* Initialize the chip */
+	eth16i_initialize(dev);  
 
-  /* Now enable Transmitter and Receiver sections */
-  BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
- 
-  /* Now switch to register bank 2, for run time operation */
-  eth16i_select_regbank(2, ioaddr);
+	/* Set the transmit buffer size */
+	lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03];
 
-  lp->open_time = jiffies;
-  lp->tx_started = 0;
-  lp->tx_queue = 0;
-  lp->tx_queue_len = 0;
-
-  /* Turn on interrupts*/
-  outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);  
-
-  dev->tbusy = 0;
-  dev->interrupt = 0;
-  dev->start = 1;
+	if(eth16i_debug > 0)
+		printk(KERN_DEBUG "%s: transmit buffer size %d\n", 
+		       dev->name, lp->tx_buf_size);
 
-#ifdef MODULE
-  MOD_INC_USE_COUNT;
-#endif
+	/* Now enable Transmitter and Receiver sections */
+	BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+
+	/* Now switch to register bank 2, for run time operation */
+	eth16i_select_regbank(2, ioaddr);
 
-  return 0;
+	lp->open_time = jiffies;
+	lp->tx_started = 0;
+	lp->tx_queue = 0;
+	lp->tx_queue_len = 0;
+
+	/* Turn on interrupts*/
+	outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);  
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
 }
 
 static int eth16i_close(struct device *dev)
 {
-  struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
-  int ioaddr = dev->base_addr;
+	struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+	int ioaddr = dev->base_addr;
 
-  lp->open_time = 0;
+	eth16i_reset(dev);
 
-  dev->tbusy = 1;
-  dev->start = 0;
+	/* Turn off interrupts*/
+	outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);  
 
-  /* Disable transmit and receive */
-  BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+	dev->start = 0;
+	dev->tbusy = 1;
 
-  /* Reset the chip */
-  outb(0xff, ioaddr + RESET);
+	lp->open_time = 0;
 
-  /* Save some energy by switching off power */
-  BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
+	/* Disable transmit and receive */
+	BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
 
-#ifdef MODULE
-  MOD_DEC_USE_COUNT;
-#endif
+	/* Reset the chip */
+	/* outb(0xff, ioaddr + RESET); */
+	/* outw(0xffff, ioaddr + TX_STATUS_REG);    */
+	
+	outb(0x00, ioaddr + CONFIG_REG_1);
+
+	MOD_DEC_USE_COUNT;
 
-  return 0;
+	return 0;
 }
 
 static int eth16i_tx(struct sk_buff *skb, struct device *dev)
 {
-  struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
-  int ioaddr = dev->base_addr;
-
-  if(dev->tbusy) {
-    /* 
-       If we get here, some higher level has decided that we are broken. 
-       There should really be a "kick me" function call instead. 
-    */
-    
-    int tickssofar = jiffies - dev->trans_start;
-    if(tickssofar < TIMEOUT_TICKS)  /* Let's not rush with our timeout, */  
-      return 1;                     /* wait a couple of ticks first     */
+	struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int status = 0;
+
+	if(dev->tbusy) {
+		
+		/* 
+		   If we get here, some higher level has decided that 
+		   we are broken. There should really be a "kick me" 
+		   function call instead. 
+		   */
+
+		int tickssofar = jiffies - dev->trans_start;
+		if(tickssofar < TX_TIMEOUT)  
+			return 1;                     
+
+		outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
+
+		printk(KERN_WARNING "%s: transmit timed out with status %04x, %s ?\n", 
+		       dev->name,
+		       inw(ioaddr + TX_STATUS_REG), 
+		       (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ? 
+		       "IRQ conflict" : "network cable problem");
+
+		dev->trans_start = jiffies;
+
+		/* Let's dump all registers */
+		if(eth16i_debug > 0) { 
+			printk(KERN_DEBUG "%s: timeout: %02x %02x %02x %02x %02x %02x %02x %02x.\n",
+			       dev->name, inb(ioaddr + 0), 
+			       inb(ioaddr + 1), inb(ioaddr + 2), 
+			       inb(ioaddr + 3), inb(ioaddr + 4), 
+			       inb(ioaddr + 5),
+			       inb(ioaddr + 6), inb(ioaddr + 7));
+
+			printk(KERN_DEBUG "%s: transmit start reg: %02x. collision reg %02x\n",
+			       dev->name, inb(ioaddr + TRANSMIT_START_REG),
+			       inb(ioaddr + COL_16_REG));
+
+			printk(KERN_DEBUG "lp->tx_queue = %d\n", lp->tx_queue);
+			printk(KERN_DEBUG "lp->tx_queue_len = %d\n", lp->tx_queue_len);
+			printk(KERN_DEBUG "lp->tx_started = %d\n", lp->tx_started);
+
+		}
 
-    printk("%s: transmit timed out with status %04x, %s ?\n", dev->name,
-	   inw(ioaddr + TX_STATUS_REG), 
-	   (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ? 
-	   "IRQ conflict" : "network cable problem");
+		lp->stats.tx_errors++;
 
-    /* Let's dump all registers */
-    if(eth16i_debug > 0) { 
-      printk("%s: timeout regs: %02x %02x %02x %02x %02x %02x %02x %02x.\n",
-	     dev->name, inb(ioaddr + 0), inb(ioaddr + 1), inb(ioaddr + 2), 
-	     inb(ioaddr + 3), inb(ioaddr + 4), inb(ioaddr + 5),
-	     inb(ioaddr + 6), inb(ioaddr + 7));
+		eth16i_reset(dev);
 
+		dev->trans_start = jiffies;
 
-      printk("lp->tx_queue = %d\n", lp->tx_queue);
-      printk("lp->tx_queue_len = %d\n", lp->tx_queue_len);
-      printk("lp->tx_started = %d\n", lp->tx_started);
+		outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
 
-    }
+	}
 
-    lp->stats.tx_errors++;
+	/* 
+	   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) {
+#if LINUX_VERSION_CODE < 0x020100
+		dev_tint(dev);
+#endif
+		if(eth16i_debug > 0) 
+			printk(KERN_WARNING "%s: Missed tx-done interrupt.\n", dev->name);
+		return 0;
+	}
 
-    /* Now let's try to restart the adaptor */
-    
-    BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
-    outw(0xffff, ioaddr + RESET);
-    eth16i_initialize(dev);
-    outw(0xffff, ioaddr + TX_STATUS_REG);
-    BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
-            
-    lp->tx_started = 0;
-    lp->tx_queue = 0;
-    lp->tx_queue_len = 0;
-    
-    outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
-    
-    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 transmitter from overlapping. This could better be
-     done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
-
-  /* Turn off TX interrupts */
-  outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
-
-  if(set_bit(0, (void *)&dev->tbusy) != 0)
-    printk("%s: Transmitter access conflict.\n", dev->name);
-  else {
-    short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-    unsigned char *buf = skb->data;
-  
-    outw(length, ioaddr + DATAPORT);
-    
-    if( ioaddr < 0x1000 ) 
-    	outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
-    else {
-	unsigned char frag = length % 4;
+	/* Block a timer based transmitter from overlapping. 
+	   This could better be done with atomic_swap(1, dev->tbusy), 
+	   but set_bit() works as well. */
 
-	outsl(ioaddr + DATAPORT, buf, length >> 2);
+	set_bit(0, (void *)&lp->tx_buf_busy);
 	
-	if( frag != 0 ) {
-	  outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1);
-	  if( frag == 3 ) 
-	    outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC) + 2), 1);
-	}
-    }
-
-    lp->tx_queue++;
-    lp->tx_queue_len += length + 2;
-
-    if(lp->tx_started == 0) {
-      /* If the transmitter is idle..always trigger a transmit */
-      outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
-      lp->tx_queue = 0;
-      lp->tx_queue_len = 0;
-      dev->trans_start = jiffies;
-      lp->tx_started = 1;
-      dev->tbusy = 0;
-    }
-    else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) {
-      /* There is still more room for one more packet in tx buffer */
-      dev->tbusy = 0;
-    }
-        
-    outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
-
-    /* Turn TX interrupts back on */
-    /* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */
-  } 
-  dev_kfree_skb(skb, FREE_WRITE);
+	/* Turn off TX interrupts */
+	outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
+
+	if(test_and_set_bit(0, (void *)&dev->tbusy) != 0) {
+		printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+		status = -1;
+	}
+	else {
+		ushort length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+		unsigned char *buf = skb->data;
+
+		if( (length + 2) > (lp->tx_buf_size - lp->tx_queue_len)) {
+			if(eth16i_debug > 0) 
+				printk(KERN_WARNING "%s: Transmit buffer full.\n", dev->name);  
+		} 
+		else {
+			outw(length, ioaddr + DATAPORT);
+
+			if( ioaddr < 0x1000 ) 
+				outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+			else {
+				unsigned char frag = length % 4;
+
+				outsl(ioaddr + DATAPORT, buf, length >> 2);
+
+				if( frag != 0 ) {
+					outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1);
+					if( frag == 3 ) 
+						outsw(ioaddr + DATAPORT, 
+						      (buf + (length & 0xFFFC) + 2), 1);
+				}
+			}
+
+			lp->tx_buffered_packets++;
+			lp->tx_queue++;
+			lp->tx_queue_len += length + 2;
+			
+		}
+		
+		lp->tx_buf_busy = 0;
+
+		if(lp->tx_started == 0) {
+			/* If the transmitter is idle..always trigger a transmit */
+			outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
+			lp->tx_queue = 0;
+			lp->tx_queue_len = 0;
+			dev->trans_start = jiffies;
+			lp->tx_started = 1;
+			dev->tbusy = 0;
+		}
+		else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) {
+			/* There is still more room for one more packet in tx buffer */
+			dev->tbusy = 0;
+		}
+		
+		outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+		
+		/* Turn TX interrupts back on */
+		/* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */
+		status = 0;
+	} 
+
+#if LINUX_VERSION_CODE >= 0x020100
+	dev_kfree_skb(skb);
+#else
+	dev_kfree_skb(skb, FREE_WRITE);
+#endif
 
-  return 0;
+	return status;
 }
 
 static void eth16i_rx(struct device *dev)
 {
-  struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
-  int ioaddr = dev->base_addr;
-  int boguscount = MAX_RX_LOOP;
+	struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int boguscount = MAX_RX_LOOP;
+
+	/* Loop until all packets have been read */
+	while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) {
+
+		/* Read status byte from receive buffer */ 
+		ushort status = inw(ioaddr + DATAPORT);
+
+		/* Get the size of the packet from receive buffer */
+		ushort pkt_len = inw(ioaddr + DATAPORT);
+
+		if(eth16i_debug > 4)
+			printk(KERN_DEBUG "%s: Receiving packet mode %02x status %04x.\n", 
+			       dev->name, 
+			       inb(ioaddr + RECEIVE_MODE_REG), status);
+		
+		if( !(status & PKT_GOOD) ) {
+			lp->stats.rx_errors++;
+
+			if( (pkt_len < ETH_ZLEN) || (pkt_len > ETH_FRAME_LEN) ) {
+				lp->stats.rx_length_errors++;
+				eth16i_reset(dev);
+				return;		
+			}
+			else { 
+				eth16i_skip_packet(dev);
+				lp->stats.rx_dropped++;
+			}	
+		}
+		else {   /* Ok so now we should have a good packet */
+			struct sk_buff *skb;
+
+			skb = dev_alloc_skb(pkt_len + 3);
+			if( skb == NULL ) {
+				printk(KERN_WARNING "%s: Could'n allocate memory for packet (len %d)\n", 
+				       dev->name, pkt_len);
+				eth16i_skip_packet(dev);
+				lp->stats.rx_dropped++;
+				break;
+			}
+
+			skb->dev = dev;
+			skb_reserve(skb,2);
+			
+			/* 
+			   Now let's get the packet out of buffer.
+			   size is (pkt_len + 1) >> 1, cause we are now reading words
+			   and it have to be even aligned.
+			   */ 
+			
+			if(ioaddr < 0x1000) 
+				insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), 
+				     (pkt_len + 1) >> 1);
+			else {	
+				unsigned char *buf = skb_put(skb, pkt_len);
+				unsigned char frag = pkt_len % 4;
+
+				insl(ioaddr + DATAPORT, buf, pkt_len >> 2);
+
+				if(frag != 0) {
+					unsigned short rest[2];
+					rest[0] = inw( ioaddr + DATAPORT );
+					if(frag == 3)
+						rest[1] = inw( ioaddr + DATAPORT );
+
+					memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag);
+				}
+			}
+
+			skb->protocol=eth_type_trans(skb, dev);
+			netif_rx(skb);
+			lp->stats.rx_packets++;
+
+			if( eth16i_debug > 5 ) {
+				int i;
+				printk(KERN_DEBUG "%s: Received packet of length %d.\n", 
+				       dev->name, pkt_len);
+				for(i = 0; i < 14; i++) 
+					printk(KERN_DEBUG " %02x", skb->data[i]);
+				printk(KERN_DEBUG ".\n");
+			}
 
-  /* Loop until all packets have been read */
-  while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) {
-    
-    /* Read status byte from receive buffer */ 
-    ushort status = inw(ioaddr + DATAPORT);
+		} /* else */
 
-    if(eth16i_debug > 4)
-      printk("%s: Receiving packet mode %02x status %04x.\n", 
-	     dev->name, inb(ioaddr + RECEIVE_MODE_REG), status);
-  
-      if( !(status & PKT_GOOD) ) {
-	/* Hmm..something went wrong. Let's check what error occurred */
-	lp->stats.rx_errors++;
-	if( status & PKT_SHORT     ) lp->stats.rx_length_errors++;
-	if( status & PKT_ALIGN_ERR ) lp->stats.rx_frame_errors++;
-	if( status & PKT_CRC_ERR   ) lp->stats.rx_crc_errors++;
-	if( status & PKT_RX_BUF_OVERFLOW) lp->stats.rx_over_errors++;
-      }
-      else {   /* Ok so now we should have a good packet */
-	struct sk_buff *skb;
-
-	/* Get the size of the packet from receive buffer */
-	ushort pkt_len = inw(ioaddr + DATAPORT);
-
-	if(pkt_len > ETH_FRAME_LEN) {
-	  printk("%s: %s claimed a very large packet, size of %d bytes.\n", 
-		 dev->name, cardname, pkt_len);
-	  outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
-	  lp->stats.rx_dropped++;
-	  break;
-	}
-
-	skb = dev_alloc_skb(pkt_len + 3);
-	if( skb == NULL ) {
-	  printk("%s: Couldn't allocate memory for packet (len %d)\n", 
-		 dev->name, pkt_len);
-	  outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
-	  lp->stats.rx_dropped++;
-	  break;
-	}
-	
-	skb->dev = dev;
-	skb_reserve(skb,2);
-	/* 
-	   Now let's get the packet out of buffer.
-	   size is (pkt_len + 1) >> 1, cause we are now reading words
-	   and it have to be even aligned.
-	*/ 
-
-	if( ioaddr < 0x1000) 
-	  insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), (pkt_len + 1) >> 1);
-	else {	
-	  unsigned char *buf = skb_put(skb, pkt_len);
-	  unsigned char frag = pkt_len % 4;
+		if(--boguscount <= 0)
+			break;
 
-	  insl(ioaddr + DATAPORT, buf, pkt_len >> 2);
-	
-	  if(frag != 0) {
-	  	unsigned short rest[2];
-		rest[0] = inw( ioaddr + DATAPORT );
-		if(frag == 3)
-			rest[1] = inw( ioaddr + DATAPORT );
+	} /* while */
+
+#if 0
+	{
+		int i;
+
+		for(i = 0; i < 20; i++) {
+			if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 
+			    RX_BUFFER_EMPTY)
+				break;
+			inw(ioaddr + DATAPORT);
+			outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG);
+		}
 
-		memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag);
-	  }
+		if(eth16i_debug > 1)
+			printk(KERN_DEBUG "%s: Flushed receive buffer.\n", dev->name);
 	}
-	
-        skb->protocol=eth_type_trans(skb, dev);
-	netif_rx(skb);
-	lp->stats.rx_packets++;
-         
-	if( eth16i_debug > 5 ) {
-	  int i;
-	  printk("%s: Received packet of length %d.\n", dev->name, pkt_len);
-	  for(i = 0; i < 14; i++) 
-	    printk(" %02x", skb->data[i]);
-	  printk(".\n");
+#endif
+
+	return;
+}
+
+static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = dev_id;
+	struct eth16i_local *lp;
+	int ioaddr = 0,
+		status;
+
+	if(dev == NULL) {
+		printk(KERN_WARNING "eth16i_interrupt(): irq %d for unknown device. \n", irq);
+		return;
 	}
-	
-      } /* else */
 
-    if(--boguscount <= 0)
-      break;
+	/* Turn off all interrupts from adapter */
+	outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
 
-  } /* while */
+	set_bit(0, (void *)&dev->tbusy);  	/* Set the device busy so that */
+	/* eth16i_tx wont be called */
 
-#if 0
-  {
-    int i;
+	if(dev->interrupt)
+		printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n", dev->name);
+	dev->interrupt = 1;
+
+	ioaddr = dev->base_addr;
+	lp = (struct eth16i_local *)dev->priv;
+	status = inw(ioaddr + TX_STATUS_REG);      /* Get the status */
+	outw(status, ioaddr + TX_STATUS_REG);      /* Clear status bits */
+
+	if(eth16i_debug > 3)
+		printk(KERN_DEBUG "%s: Interrupt with status %04x.\n", dev->name, status);
+
+	if( status & 0x7f00 ) {
+
+		lp->stats.rx_errors++;
+
+		if(status & (BUS_RD_ERR << 8) ) 
+			printk(KERN_WARNING "%s: Bus read error.\n",dev->name);
+		if(status & (SHORT_PKT_ERR << 8) )   lp->stats.rx_length_errors++;
+		if(status & (ALIGN_ERR << 8) )       lp->stats.rx_frame_errors++;
+		if(status & (CRC_ERR << 8) )	    lp->stats.rx_crc_errors++;
+		if(status & (RX_BUF_OVERFLOW << 8) ) lp->stats.rx_over_errors++;
+	}
+	if( status & 0x001a) {
+
+		lp->stats.tx_errors++;
+
+		if(status & CR_LOST) lp->stats.tx_carrier_errors++;
+		if(status & TX_JABBER_ERR) lp->stats.tx_window_errors++;
 
-    for(i = 0; i < 20; i++) {
-      if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == RX_BUFFER_EMPTY)
-	break;
-      inw(ioaddr + DATAPORT);
-      outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
-    }
-
-    if(eth16i_debug > 1)
-      printk("%s: Flushed receive buffer.\n", dev->name);
-  }
+#if 0	       
+		if(status & COLLISION) {
+			lp->stats.collisions += 
+				((inb(ioaddr+TRANSMIT_MODE_REG) & 0xF0) >> 4);
+		}
 #endif
+		if(status & COLLISIONS_16) {
+			if(lp->col_16 < MAX_COL_16) { 
+				lp->col_16++;
+				lp->stats.collisions++;
+				/* Resume transmitting, skip failed packet */
+				outb(0x02, ioaddr + COL_16_REG);
+			}
+			else {
+				printk(KERN_WARNING "%s: bailing out due to many consecutive 16-in-a-row collisions. Network cable problem?\n", dev->name);
+			}
+		}
+	}
+
+	if( status & 0x00ff ) {          /* Let's check the transmit status reg */
+
+		if(status & TX_DONE) {         /* The transmit has been done */
+			lp->stats.tx_packets = lp->tx_buffered_packets;
+			lp->col_16 = 0;		   
+
+			if(lp->tx_queue) {           /* Is there still packets ? */
+				/* There was packet(s) so start transmitting and write also
+				   how many packets there is to be sended */
+				outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
+				lp->tx_queue = 0;
+				lp->tx_queue_len = 0;
+				lp->tx_started = 1;
+				dev->trans_start = jiffies;
+				mark_bh(NET_BH); 
+			}
+			else {
+				lp->tx_started = 0;
+				mark_bh(NET_BH);
+			}
+		}
+	}
+
+	if( ( status & 0x8000 ) || 
+	    ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) {
+		eth16i_rx(dev);  /* We have packet in receive buffer */
+	}  
+	
+	dev->interrupt = 0;
+	
+	/* Turn interrupts back on */
+	outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+	
+	if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) {
+		/* There is still more room for one more packet in tx buffer */
+		dev->tbusy = 0;
+	}
+	
+	return;
+}
+
+static void eth16i_skip_packet(struct device *dev)
+{	 
+	int ioaddr = dev->base_addr;
 
-  return;
+	inw(ioaddr + DATAPORT);
+	inw(ioaddr + DATAPORT);
+	inw(ioaddr + DATAPORT);
+
+	outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG);
+	while( inb( ioaddr + FILTER_SELF_RX_REG ) != 0);
 }
 
-static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void eth16i_reset(struct device *dev)
 {
-  struct device *dev = (struct device *)(irq2dev_map[irq]);
-  struct eth16i_local *lp;
-  int ioaddr = 0,
-      status;
-
-  if(dev == NULL) {
-    printk("eth16i_interrupt(): irq %d for unknown device. \n", irq);
-    return;
-  }
-
-  /* Turn off all interrupts from adapter */
-  outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); 
-
-  dev->interrupt = 1;
-
-  ioaddr = dev->base_addr;
-  lp = (struct eth16i_local *)dev->priv;
-  status = inw(ioaddr + TX_STATUS_REG);      /* Get the status */
-  outw(status, ioaddr + TX_STATUS_REG);      /* Clear status bits */
-  
-  if(eth16i_debug > 3)
-    printk("%s: Interrupt with status %04x.\n", dev->name, status);
+	struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+	int ioaddr = dev->base_addr;
 
-  if( status & 0x00ff ) {          /* Let's check the transmit status reg */
-    
-    if(status & TX_DONE) {         /* The transmit has been done */
-      lp->stats.tx_packets++;
+	if(eth16i_debug > 1) 
+		printk(KERN_DEBUG "%s: Resetting device.\n", dev->name);
 
-      if(lp->tx_queue) {           /* Is there still packets ? */
-	  /* There was packet(s) so start transmitting and write also
-	   how many packets there is to be sent */
-	outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
-	lp->tx_queue = 0;
-	lp->tx_queue_len = 0;
-	dev->trans_start = jiffies;
-	dev->tbusy = 0;
-	mark_bh(NET_BH);
-      }
-      else {
-	lp->tx_started = 0;
-	dev->tbusy = 0;
-	mark_bh(NET_BH);
-      }
-    }
-  }
-    
-  if( ( status & 0xff00 ) || 
-     ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) {
-    eth16i_rx(dev);  /* We have packet in receive buffer */
-  }
+	BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+	outw(0xffff, ioaddr + TX_STATUS_REG);    
+	eth16i_select_regbank(2, ioaddr);
 
-  dev->interrupt = 0;
-  
-  /* Turn interrupts back on */
-  outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+	lp->tx_started = 0;
+	lp->tx_buf_busy = 0;
+	lp->tx_queue = 0;
+	lp->tx_queue_len = 0;    
 
-  return;
+	dev->interrupt = 0;
+	dev->start = 1;
+	dev->tbusy = 0;
+	BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
 }
 
 static void eth16i_multicast(struct device *dev)
 {
-  short ioaddr = dev->base_addr;
+	int ioaddr = dev->base_addr;
   
-  if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) 
-  {
-    dev->flags|=IFF_PROMISC;	/* Must do this */
-    outb(3, ioaddr + RECEIVE_MODE_REG);    
-  } else {
-    outb(2, ioaddr + RECEIVE_MODE_REG);
-  }
+	if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) 
+	{
+		dev->flags|=IFF_PROMISC;	/* Must do this */
+		outb(3, ioaddr + RECEIVE_MODE_REG);    
+	} else {
+		outb(2, ioaddr + RECEIVE_MODE_REG);
+	}
 }
 
 static struct enet_statistics *eth16i_get_stats(struct device *dev)
 {
-  struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+	struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
 
-  return &lp->stats;
+	return &lp->stats;
 }
 
-static void eth16i_select_regbank(unsigned char banknbr, short ioaddr)
+static void eth16i_select_regbank(unsigned char banknbr, int ioaddr)
 {
-  unsigned char data;
+	unsigned char data;
 
-  data = inb(ioaddr + CONFIG_REG_1);
-  outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1); 
+	data = inb(ioaddr + CONFIG_REG_1);
+	outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1); 
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_eth16i = {
-	devicename,
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, eth16i_probe };
 
-int io = 0x2a0;
-int irq = 0;
+static ushort eth16i_parse_mediatype(const char* s)
+{
+	if(!s)
+		return E_PORT_FROM_EPROM;
+	
+        if (!strncmp(s, "bnc", 3))
+		return E_PORT_BNC;
+        else if (!strncmp(s, "tp", 2))
+                return E_PORT_TP;
+        else if (!strncmp(s, "dix", 3))
+                return E_PORT_DIX;
+        else if (!strncmp(s, "auto", 4))
+		return E_PORT_AUTO;
+	else
+		return E_PORT_FROM_EPROM;
+}
+
+#define MAX_ETH16I_CARDS 4  /* Max number of Eth16i cards per module */
+#define NAMELEN          8  /* number of chars for storing dev->name */
+
+static char namelist[NAMELEN * MAX_ETH16I_CARDS] = { 0, };
+static struct device dev_eth16i[MAX_ETH16I_CARDS] = {
+	{
+		NULL,
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int ioaddr[MAX_ETH16I_CARDS] = { 0, };
+#if 0
+static int irq[MAX_ETH16I_CARDS] = { 0, };
+#endif
+static char* mediatype[MAX_ETH16I_CARDS] = { 0, };
+static int debug = -1;
+
+#if (LINUX_VERSION_CODE >= 0x20115) 
+MODULE_AUTHOR("Mika Kuoppala <miku@iki.fi>");
+MODULE_DESCRIPTION("ICL EtherTeam 16i/32 driver");
+
+MODULE_PARM(ioaddr, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i");
+MODULE_PARM_DESC(ioaddr, "eth16i io base address");
+
+#if 0
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i");
+MODULE_PARM_DESC(irq, "eth16i interrupt request number");
+#endif
+
+MODULE_PARM(mediatype, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "s");
+MODULE_PARM_DESC(mediatype, "eth16i interfaceport mediatype");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "eth16i debug level (0-4)");
+#endif
 
 int init_module(void)
 {
-	if(io == 0)
-		printk("eth16i: You should not use auto-probing with insmod!\n");
+	int this_dev, found = 0;
+
+	for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++)
+	{
+		struct device *dev = &dev_eth16i[this_dev];
 	
-	dev_eth16i.base_addr = io;
-	dev_eth16i.irq = irq;
-	if( register_netdev( &dev_eth16i ) != 0 ) {
-		printk("eth16i: register_netdev() returned non-zero.\n");
-		return -EIO;
-	}
+		dev->name = namelist + (NAMELEN*this_dev);
+		dev->irq = 0; /* irq[this_dev]; */
+		dev->base_addr = ioaddr[this_dev];
+		dev->init = eth16i_probe;
+
+	        if(debug != -1)
+			eth16i_debug = debug;
+
+		if(eth16i_debug > 1)
+			printk(KERN_NOTICE "eth16i(%d): interface type %s\n", this_dev, mediatype[this_dev] ? mediatype[this_dev] : "none" );
+
+		dev->if_port = eth16i_parse_mediatype(mediatype[this_dev]);
+
+		if(ioaddr[this_dev] == 0)
+		{
+			if(this_dev != 0) break; /* Only autoprobe 1st one */
+
+			printk(KERN_NOTICE "eth16i.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+
+		if(register_netdev(dev) != 0)
+		{
+			printk(KERN_WARNING "eth16i.c No Eth16i card found (i/o = 0x%x).\n",
+			       ioaddr[this_dev]);
+	    
+			if(found != 0) return 0;
+			return -ENXIO;
+		}
 
+		found++;
+	}
 	return 0;
 }
-
+	
 void cleanup_module(void)
 {
-	unregister_netdev( &dev_eth16i );
-	free_irq( dev_eth16i.irq, NULL );
-	irq2dev_map[ dev_eth16i.irq ] = NULL;
-	release_region( dev_eth16i.base_addr, ETH16I_IO_EXTENT );
-}
+	int this_dev;
 
+	for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++)
+	{
+		struct device* dev = &dev_eth16i[this_dev];
+		
+		if(dev->priv != NULL)
+		{
+			unregister_netdev(dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
+			
+			free_irq(dev->irq, dev);
+			release_region(dev->base_addr, ETH16I_IO_EXTENT);
+			
+		}
+	}
+}
 #endif /* MODULE */
 
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c eth16i.c"
+ *  alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict -prototypes -O6 -c eth16i.c"
+ *  tab-width: 8
+ *  c-basic-offset: 8
+ *  c-indent-level: 8
+ * End:
+ */
 
+/* End of file eth16i.c */
diff -u --recursive --new-file v2.0.35/linux/drivers/net/hp100.c linux/drivers/net/hp100.c
--- v2.0.35/linux/drivers/net/hp100.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/hp100.c	Sun Nov 15 10:33:03 1998
@@ -1044,7 +1044,7 @@
           
           /*
            * 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
+           * 4 bytes 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. 
diff -u --recursive --new-file v2.0.35/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v2.0.35/linux/drivers/net/ibmtr.c	Tue Dec  2 13:52:31 1997
+++ linux/drivers/net/ibmtr.c	Sun Nov 15 10:33:03 1998
@@ -439,9 +439,9 @@
 	/* We must figure out how much shared memory space this adapter
 	   will occupy so that if there are two adapters we can fit both
 	   in.  Given a choice, we will limit this adapter to 32K.  The
-	   maximum space will will use for two adapters is 64K so if the
-	   adapter we are working on demands 64K (it also doesn't support
-	   paging), then only one adapter can be supported.  */
+	   maximum space will use for two adapters is 64K so if the adapter
+	   we are working on demands 64K (it also doesn't support paging),
+	   then only one adapter can be supported.  */
 	
 	/* determine how much of total RAM is mapped into PC space */
 	ti->mapped_ram_size=1<<((((readb(ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD)) >>2) & 0x03) + 4);
diff -u --recursive --new-file v2.0.35/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v2.0.35/linux/drivers/net/lance.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/lance.c	Sun Nov 15 10:33:03 1998
@@ -297,7 +297,7 @@
 
 static int lance_open(struct device *dev);
 static int lance_open_fail(struct device *dev);
-static void lance_init_ring(struct device *dev);
+static void lance_init_ring(struct device *dev, int mode);
 static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
 static int lance_rx(struct device *dev);
 static void lance_interrupt(int irq, void *dev_id, struct pt_regs *regs);
@@ -360,7 +360,7 @@
 		if (dev->priv != NULL) {
 			kfree(dev->priv);
 			dev->priv = NULL;
-			free_irq(dev->irq, NULL);
+			free_dma(dev->dma);
 			release_region(dev->base_addr, LANCE_TOTAL_SIZE);
 			unregister_netdev(dev);
 		}
@@ -512,14 +512,6 @@
 	dev->base_addr = ioaddr;
 	request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name);
 
-#ifdef CONFIG_LANCE32
-	/* look if it's a PCI or VLB chip */
-	if (lance_version == PCNET_PCI || lance_version == PCNET_VLB || lance_version == PCNET_PCI_II) {
-	    extern int lance32_probe1 (struct device *dev, const char *chipname, int pci_irq_line);
-	    
-	    return lance32_probe1 (dev, chipname, pci_irq_line);
-	}
-#endif    
 	/* Make certain the data structures used by the LANCE are aligned and DMAble. */
 		
 	lp = (struct lance_private *)(((unsigned long)kmalloc(sizeof(*lp)+7,
@@ -746,7 +738,7 @@
 		           (u32) virt_to_bus(lp->rx_ring),
 			   (u32) virt_to_bus(&lp->init_block));
 
-	lance_init_ring(dev);
+	lance_init_ring(dev, GFP_KERNEL);
 	/* Re-initialize the LANCE, and start it when done. */
 	outw(0x0001, ioaddr+LANCE_ADDR);
 	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
@@ -808,7 +800,7 @@
 
 /* Initialize the LANCE Rx and Tx rings. */
 static void
-lance_init_ring(struct device *dev)
+lance_init_ring(struct device *dev, int gfp)
 {
 	struct lance_private *lp = (struct lance_private *)dev->priv;
 	int i;
@@ -821,13 +813,13 @@
 		struct sk_buff *skb;
 		void *rx_buff;
 
-		skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | GFP_KERNEL);
+		skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp);
 		lp->rx_skbuff[i] = skb;
 		if (skb) {
 			skb->dev = dev;
 			rx_buff = skb->tail;
 		} else
-			rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | GFP_KERNEL);
+			rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp);
 		if (rx_buff == NULL)
 			lp->rx_ring[i].base = 0;
 		else
@@ -858,7 +850,7 @@
 	if (must_reinit ||
 		(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
 		lance_purge_tx_ring(dev);
-		lance_init_ring(dev);
+		lance_init_ring(dev, GFP_ATOMIC);
 	}
 	outw(0x0000,    dev->base_addr + LANCE_ADDR);
 	outw(csr0_bits, dev->base_addr + LANCE_DATA);
diff -u --recursive --new-file v2.0.35/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c
--- v2.0.35/linux/drivers/net/ne2k-pci.c	Sun Nov 15 10:49:39 1998
+++ linux/drivers/net/ne2k-pci.c	Sun Nov 15 10:33:03 1998
@@ -30,7 +30,6 @@
 #include <linux/modversions.h>
 #endif
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
diff -u --recursive --new-file v2.0.35/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- v2.0.35/linux/drivers/net/pcnet32.c	Mon Jul 13 13:46:29 1998
+++ linux/drivers/net/pcnet32.c	Sun Nov 15 10:33:03 1998
@@ -27,7 +27,6 @@
 #define PCNET_LOG_TX_BUFFERS 4
 #define PCNET_LOG_RX_BUFFERS 4
 
-#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
@@ -179,7 +178,8 @@
 	{0x2420, "PCnet/PCI 79C970", 0},
 	{0x2430, "PCnet32", 0},
 	{0x2621, "PCnet/PCI II 79C970A", 0},
-	{0x2623, "PCnet/PCI II 79C971A", 0},
+	{0x2623, "PCnet/FAST 79C971", 0},
+	{0x2624, "PCnet/FAST+ 79C972", 0},
 	{0x0, 	 "PCnet32 (unknown)", 0},
 };
 
@@ -242,11 +242,6 @@
 									  PCI_COMMAND, new_command);
 		}
 
-#ifdef __powerpc__
-		/* This is bogus! -djb */
-		irq_line = 15;
-#endif
-
 		if (pcnet32_probe1(dev, pci_ioaddr, irq_line) != 0) {
 			/* Should never happen. */
 			printk(KERN_ERR "pcnet32.c: Probe of PCI card at %#x failed.\n",
@@ -305,6 +300,8 @@
 	   The first six bytes are the station address. */
 	for (i = 0; i < 6; i++)
 		printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+		
+	printk("\n");
 
 	dev->base_addr = ioaddr;
 	request_region(ioaddr, PCNET32_TOTAL_SIZE, dev->name);
@@ -344,15 +341,7 @@
     outw(0x0000, ioaddr+PCNET32_ADDR);
     inw(ioaddr+PCNET32_ADDR);
 
-	dev->irq = irq_line;
-#ifdef __powerpc__
-		/* This is sooo bogus! -djb */
-	    irq_line = 15;
-#endif
-
-    outw(0x0002, ioaddr+PCNET32_ADDR);
-    /* only touch autoselect bit */
-    outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF);
+    dev->irq = irq_line;
 
     if (pcnet32_debug > 0)
       printk(version);
@@ -403,7 +392,17 @@
 		           (u32) virt_to_bus(lp->rx_ring),
 			   (u32) virt_to_bus(&lp->init_block));
 
-	lp->init_block.mode = 0x0000;
+	/* check for ATLAS T1/E1 LAW card */
+	if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75) {
+		/* select GPSI mode */
+		lp->init_block.mode = 0x0100;
+		outw(0x0002, ioaddr+PCNET32_ADDR);
+		outw(inw(ioaddr+PCNET32_BUS_IF) & ~2, ioaddr+PCNET32_BUS_IF);
+		/* switch full duplex on */
+		outw(0x0009, ioaddr+PCNET32_ADDR);
+		outw(inw(ioaddr+PCNET32_BUS_IF) | 1, ioaddr+PCNET32_BUS_IF);
+	} else
+		lp->init_block.mode = 0x0000;
 	lp->init_block.filter[0] = 0x00000000;
 	lp->init_block.filter[1] = 0x00000000;
 	pcnet32_init_ring(dev);
@@ -718,7 +717,22 @@
 
 		/* Log misc errors. */
 		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
-		if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
+		if (csr0 & 0x1000) {
+			/*
+			 * this happens when our receive ring is full. This 
+			 * shouldn't be a problem as we will see normal rx 
+			 * interrupts for the frames in the receive ring. But 
+			 * there are some PCI chipsets (I can reproduce this 
+			 * on SP3G with Intel saturn chipset) which have some-
+			 * times problems and will fill up the receive ring 
+			 * with error descriptors. In this situation we don't 
+			 * get a rx interrupt, but a missed frame interrupt 
+			 * sooner or later. So we try to clean up our receive 
+			 * ring here.
+			 */
+			pcnet32_rx(dev);
+			lp->stats.rx_errors++; /* Missed a Rx frame. */
+		}
 		if (csr0 & 0x0800) {
 			printk("%s: Bus master arbitration failure, status %4.4x.\n",
 				   dev->name, csr0);
@@ -878,14 +892,14 @@
 	if (dev->flags&IFF_PROMISC) {
 		/* Log any net taps. */
 		printk("%s: Promiscuous mode enabled.\n", dev->name);
-		lp->init_block.mode = 0x8000;
+		lp->init_block.mode |= 0x8000;
 	} else {
 		int num_addrs=dev->mc_count;
 		if(dev->flags&IFF_ALLMULTI)
 			num_addrs=1;
 		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
 		memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter));
-		lp->init_block.mode = 0x0000;
+		lp->init_block.mode &= ~0x8000;
 	}
 
 	outw(0, ioaddr+PCNET32_ADDR);
diff -u --recursive --new-file v2.0.35/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.0.35/linux/drivers/net/plip.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/plip.c	Sun Nov 15 10:33:03 1998
@@ -390,7 +390,7 @@
 				return TIMEOUT;
 			}
 			c0 = inb(PAR_STATUS(dev));
-			printk("%s: transmit timeout(%d,%02x)\n",
+			printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n",
 			       dev->name, snd->state, c0);
 		}
 		nl->enet_stats.tx_errors++;
@@ -408,7 +408,7 @@
 				return TIMEOUT;
 			}
 			c0 = inb(PAR_STATUS(dev));
-			printk("%s: receive timeout(%d,%02x)\n",
+			printk(KERN_WARNING "%s: receive timeout(%d,%02x)\n",
 			       dev->name, rcv->state, c0);
 		}
 		nl->enet_stats.rx_dropped++;
@@ -538,13 +538,13 @@
 			return TIMEOUT;
 		if (rcv->length.h > dev->mtu + dev->hard_header_len
 		    || rcv->length.h < 8) {
-			printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
+			printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h);
 			return ERROR;
 		}
 		/* Malloc up new buffer. */
 		rcv->skb = dev_alloc_skb(rcv->length.h);
 		if (rcv->skb == NULL) {
-			printk("%s: Memory squeeze.\n", dev->name);
+			printk(KERN_WARNING "%s: Memory squeeze.\n", dev->name);
 			return ERROR;
 		}
 		skb_put(rcv->skb,rcv->length.h);
@@ -668,7 +668,7 @@
 	unsigned int cx;
 
 	if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
-		printk("%s: send skb lost\n", dev->name);
+		printk(KERN_ERR "%s: send skb lost\n", dev->name);
 		snd->state = PLIP_PK_DONE;
 		snd->skb = NULL;
 		return ERROR;
@@ -811,7 +811,7 @@
 	unsigned char c0;
 
 	if (dev == NULL) {
-		printk ("plip_interrupt: irq %d for unknown device.\n", irq);
+		printk (KERN_ERR "plip_interrupt: irq %d for unknown device.\n", irq);
 		return;
 	}
 
@@ -845,12 +845,12 @@
 
 	case PLIP_CN_RECEIVE:
 		sti();
-		printk("%s: receive interrupt when receiving packet\n", dev->name);
+		printk(KERN_WARNING "%s: receive interrupt when receiving packet\n", dev->name);
 		break;
 
 	case PLIP_CN_ERROR:
 		sti();
-		printk("%s: receive interrupt in error state\n", dev->name);
+		printk(KERN_WARNING "%s: receive interrupt in error state\n", dev->name);
 		break;
 	}
 }
@@ -868,7 +868,7 @@
 		return nl->orig_rebuild_header(buff, dev, dst, skb);
 
 	if (eth->h_proto != htons(ETH_P_IP)) {
-		printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
+		printk(KERN_WARNING "plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
 		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
 		return 0;
 	}
@@ -897,12 +897,12 @@
 	}
 
 	if (set_bit(0, (void*)&dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
+		printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
 		return 1;
 	}
 
 	if (skb->len > dev->mtu + dev->hard_header_len) {
-		printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
+		printk(KERN_WARNING "%s: packet too big, %d.\n", dev->name, (int)skb->len);
 		dev->tbusy = 0;
 		dev_kfree_skb(skb, FREE_WRITE);
 		return 0;
@@ -940,13 +940,13 @@
 	int i;
 
 	if (dev->irq == 0) {
-		printk("%s: IRQ is not set.  Please set it by ifconfig.\n", dev->name);
+		printk(KERN_INFO "%s: IRQ is not set.  Please set it by ifconfig.\n", dev->name);
 		return -EAGAIN;
 	}
 	cli();
 	if (request_irq(dev->irq , plip_interrupt, 0, dev->name, NULL) != 0) {
 		sti();
-		printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
+		printk(KERN_WARNING "%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
 		return -EAGAIN;
 	}
 	irq2dev_map[dev->irq] = dev;
@@ -1029,7 +1029,7 @@
 
 	if (map->base_addr != (unsigned long)-1
 	    && map->base_addr != dev->base_addr)
-		printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
+		printk(KERN_WARNING "%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
 
 	if (map->irq != (unsigned char)-1)
 		dev->irq = map->irq;
diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcif.h linux/drivers/net/rcif.h
--- v2.0.35/linux/drivers/net/rcif.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rcif.h	Sun Nov 15 10:33:03 1998
@@ -0,0 +1,235 @@
+/*
+** *************************************************************************
+**
+**
+**     R C I F . H
+**
+**
+**  RedCreek InterFace include file.
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1998, RedCreek Communications Inc.          ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+** File Description:
+**
+** Header file private ioctl commands.
+**
+**
+**  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 RCIF_H
+#define RCIF_H
+
+/* The following protocol revision # should be incremented every time
+   a new protocol or new structures are used in this file. */
+int USER_PROTOCOL_REV = 1;     /* used to track different protocol revisions */
+
+/* define a single TCB & buffer */
+typedef struct                  /* a single buffer */
+{
+     U32 context;               /* context */
+     U32 scount;                /* segment count */
+     U32 size;                  /* segment size */
+     U32 addr;                  /* segment physical address */
+}
+__attribute__((packed))
+singleB, *psingleB ;
+typedef struct                  /* a single TCB */
+{
+     /*
+     **  +-----------------------+
+     **  |         1             |  one buffer in the TCB
+     **  +-----------------------+
+     **  |  <user's Context>     |  user's buffer reference
+     **  +-----------------------+
+     **  |         1             |  one segment buffer
+     **  +-----------------------+                            _
+     **  |    <buffer size>      |  size                       \ 
+     **  +-----------------------+                              \ segment descriptor
+     **  |  <physical address>   |  physical address of buffer  /
+     **  +-----------------------+                            _/
+     */
+     U32 bcount;                /* buffer count */
+     singleB b;                 /* buffer */
+
+}
+__attribute__((packed))
+singleTCB, *psingleTCB;
+
+/*
+   When adding new entries, please add all 5 related changes, since 
+   it helps keep everything consistent:
+      1) User structure entry
+      2) User data entry
+      3) Structure short-cut entry
+      4) Data short-cut entry
+      5) Command identifier entry
+
+   For Example ("GETSPEED"):
+      1) struct  RCgetspeed_tag { U32 LinkSpeedCode; } RCgetspeed;
+      2) struct  RCgetspeed_tag *getspeed;
+      3) #define RCUS_GETSPEED  data.RCgetspeed;
+      4) #define RCUD_GETSPEED  _RC_user_data.getspeed
+      5) #define RCUC_GETSPEED  0x02
+  
+   Notes for the "GETSPEED" entry, above:
+      1) RCgetspeed      - RC{name}
+         RCgetspeed_tag  - RC{name}_tag
+         LinkSpeedCode   - create any structure format desired (not too large,
+                           since memory will be unioned with all other entries)
+      2) RCgetspeed_tag  - RC{name}_tag chosen in #1
+         getspeed        - arbitrary name (ptr to structure in #1)
+      3) RCUS_GETSPEED   - RCUS_{NAME}   ("NAME" & "name" do not have to the same)
+         data.RCgetspeed - data.RC{name}  ("RC{name}" from #1)
+      4) RCUD_GETSPEED   - _RC_user_data.getspeed  ("getspeed" from #2)
+      5) RCUC_GETSPEED   - unique hex identifier entry.
+*/
+
+typedef struct RC_user_tag RCuser_struct;
+
+/* 1) User structure entry */
+struct RC_user_tag
+{
+    int cmd;
+    union
+    {
+        /* GETINFO structure */
+        struct RCgetinfo_tag {
+            unsigned long int mem_start;
+            unsigned long int mem_end;
+            unsigned long int base_addr;
+            unsigned char irq;
+            unsigned char dma;
+            unsigned char port;
+        } RCgetinfo;                    /* <---- RCgetinfo */
+     
+        /* GETSPEED structure */
+        struct  RCgetspeed_tag {
+            U32 LinkSpeedCode;
+        } RCgetspeed;                   /* <---- RCgetspeed */
+  
+        /* GETFIRMWAREVER structure */
+        #define FirmStringLen 80
+        struct RCgetfwver_tag {
+            U8 FirmString[FirmStringLen];
+        } RCgetfwver;                   /* <---- RCgetfwver */
+  
+        /* GETIPANDMASK structure */
+        struct RCgetipnmask_tag {
+            U32 IpAddr;
+            U32 NetMask;
+        } RCgetipandmask;               /* <---- RCgetipandmask */
+
+        /* GETMAC structure */
+        #define MAC_SIZE 10
+        struct RCgetmac_tag {
+            U8 mac[MAC_SIZE];
+        } RCgetmac;                     /* <---- RCgetmac */
+
+        /* GETLINKSTATUS structure */
+        struct RCgetlnkstatus_tag {
+            U32 ReturnStatus;
+        } RCgetlnkstatus;               /* <---- RCgetlnkstatus */
+
+        /* GETLINKSTATISTICS structure */
+        struct RCgetlinkstats_tag {
+            RCLINKSTATS StatsReturn;
+        } RCgetlinkstats;               /* <---- RCgetlinkstats */
+
+        /* DEFAULT structure (when no command was recognized) */
+        struct RCdefault_tag {
+            int rc;
+        } RCdefault;                    /* <---- RCdefault */
+
+    } data;
+
+};   /* struct RC_user_tag { ... } */
+
+/* 2) User data entry */
+/* RCUD = RedCreek User Data */
+union RC_user_data_tag {        /* structure tags used are taken from RC_user_tag structure above */
+    struct RCgetinfo_tag      *getinfo;
+    struct RCgetspeed_tag     *getspeed;
+    struct RCgetfwver_tag     *getfwver;
+    struct RCgetipnmask_tag   *getipandmask;
+    struct RCgetmac_tag       *getmac;
+    struct RCgetlnkstatus_tag *getlinkstatus;
+    struct RCgetlinkstats_tag *getlinkstatistics;
+    struct RCdefault_tag      *rcdefault;
+} _RC_user_data;  /* declare as a global, so the defines below will work */
+
+/* 3) Structure short-cut entry */
+/* define structure short-cuts */   /* structure names are taken from RC_user_tag structure above */
+#define RCUS_GETINFO           data.RCgetinfo;
+#define RCUS_GETSPEED          data.RCgetspeed;
+#define RCUS_GETFWVER          data.RCgetfwver;
+#define RCUS_GETIPANDMASK      data.RCgetipandmask;
+#define RCUS_GETMAC            data.RCgetmac;
+#define RCUS_GETLINKSTATUS     data.RCgetlnkstatus;
+#define RCUS_GETLINKSTATISTICS data.RCgetlinkstats;
+#define RCUS_DEFAULT           data.RCdefault;
+
+/* 4) Data short-cut entry */
+/* define data short-cuts */    /* pointer names are from RC_user_data_tag union (just below RC_user_tag) */
+#define RCUD_GETINFO           _RC_user_data.getinfo
+#define RCUD_GETSPEED          _RC_user_data.getspeed
+#define RCUD_GETFWVER          _RC_user_data.getfwver
+#define RCUD_GETIPANDMASK      _RC_user_data.getipandmask
+#define RCUD_GETMAC            _RC_user_data.getmac
+#define RCUD_GETLINKSTATUS     _RC_user_data.getlinkstatus
+#define RCUD_GETLINKSTATISTICS _RC_user_data.getlinkstatistics
+#define RCUD_DEFAULT           _RC_user_data.rcdefault
+
+/* 5) Command identifier entry */
+/* define command identifiers */
+#define RCUC_GETINFO            0x01
+#define RCUC_GETSPEED           0x02
+#define RCUC_GETFWVER           0x03
+#define RCUC_GETIPANDMASK       0x04
+#define RCUC_GETMAC             0x05
+#define RCUC_GETLINKSTATUS      0x06
+#define RCUC_GETLINKSTATISTICS  0x07
+#define RCUC_DEFAULT            0xff
+
+/* define ioctl commands to use, when talking to RC 45/PCI driver */
+#define RCU_PROTOCOL_REV         SIOCDEVPRIVATE
+#define RCU_COMMAND              SIOCDEVPRIVATE+1
+
+/*
+   Intended use for the above defines is shown below (GETINFO, as this example):
+
+      RCuser_struct RCuser;           // declare RCuser structure
+      struct ifreq ifr;               // declare an interface request structure
+
+      RCuser.cmd = RCUC_GETINFO;           // set user command to GETINFO
+      ifr->ifr_data = (caddr_t) &RCuser;   // set point to user structure
+
+      sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);   // get a socket
+      ioctl(sock, RCU_COMMAND, &ifr);                  // do ioctl on socket
+
+      RCUD_GETINFO = &RCuser.RCUS_GETINFO;   // set data pointer for GETINFO
+
+      // print results
+      printf("memory 0x%lx-0x%lx, base address 0x%x, irq 0x%x\n",
+              RCUD_GETINFO->mem_start, RCUD_GETINFO->mem_end,
+              RCUD_GETINFO->base_addr, RCUD_GETINFO->irq);
+*/
+
+#endif   /* RCIF_H */
+
diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcmtl.c linux/drivers/net/rcmtl.c
--- v2.0.35/linux/drivers/net/rcmtl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rcmtl.c	Sun Nov 15 10:33:03 1998
@@ -0,0 +1,2058 @@
+/*
+** *************************************************************************
+**
+**
+**     R C M T L . C             $Revision: 1.1 $
+**
+**
+**  RedCreek Message Transport Layer program module.
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1997-1998, RedCreek Communications Inc.     ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+** File Description:
+**
+** Host side message transport layer.
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+***************************************************************************/
+
+#undef DEBUG
+
+#define RC_LINUX_MODULE
+#include "rcmtl.h"
+
+#define dprintf kprintf
+
+extern int printk(const char * fmt, ...);
+
+ /* RedCreek LAN device Target ID */
+#define LAN_TARGET_ID  0x10 
+ /* RedCreek's OSM default LAN receive Initiator */
+#define DEFAULT_RECV_INIT_CONTEXT  0xA17  
+
+
+/*
+** message structures
+*/
+
+#define    TID_SZ                                  12
+#define    FUNCTION_SZ                             8
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+
+#define    TRL_SINGLE_FIXED_LENGTH           0x00
+#define    TRL_SINGLE_VARIABLE_LENGTH        0x40
+#define    TRL_MULTIPLE_FIXED_LENGTH         0x80
+
+/* LAN Class specific functions */
+
+#define    LAN_PACKET_SEND                         0x3B
+#define    LAN_SDU_SEND                            0x3D
+#define    LAN_RECEIVE_POST                        0x3E
+#define    LAN_RESET                               0x35
+#define    LAN_SHUTDOWN                            0x37
+
+/* Private Class specfic function */
+#define    RC_PRIVATE                                 0xFF
+
+/*  RC Executive Function Codes.  */
+
+#define    RC_CMD_ADAPTER_ASSIGN                     0xB3
+#define    RC_CMD_ADAPTER_READ                       0xB2
+#define    RC_CMD_ADAPTER_RELEASE                    0xB5
+#define    RC_CMD_BIOS_INFO_SET                      0xA5
+#define    RC_CMD_BOOT_DEVICE_SET                    0xA7
+#define    RC_CMD_CONFIG_VALIDATE                    0xBB
+#define    RC_CMD_CONN_SETUP                         0xCA
+#define    RC_CMD_DEVICE_ASSIGN                      0xB7
+#define    RC_CMD_DEVICE_RELEASE                     0xB9
+#define    RC_CMD_HRT_GET                            0xA8
+#define    RC_CMD_ADAPTER_CLEAR                          0xBE
+#define    RC_CMD_ADAPTER_CONNECT                        0xC9
+#define    RC_CMD_ADAPTER_RESET                          0xBD
+#define    RC_CMD_LCT_NOTIFY                         0xA2
+#define    RC_CMD_OUTBOUND_INIT                      0xA1
+#define    RC_CMD_PATH_ENABLE                        0xD3
+#define    RC_CMD_PATH_QUIESCE                       0xC5
+#define    RC_CMD_PATH_RESET                         0xD7
+#define    RC_CMD_STATIC_MF_CREATE                   0xDD
+#define    RC_CMD_STATIC_MF_RELEASE                  0xDF
+#define    RC_CMD_STATUS_GET                         0xA0
+#define    RC_CMD_SW_DOWNLOAD                        0xA9
+#define    RC_CMD_SW_UPLOAD                          0xAB
+#define    RC_CMD_SW_REMOVE                          0xAD
+#define    RC_CMD_SYS_ENABLE                         0xD1
+#define    RC_CMD_SYS_MODIFY                         0xC1
+#define    RC_CMD_SYS_QUIESCE                        0xC3
+#define    RC_CMD_SYS_TAB_SET                        0xA3
+
+
+ /* Init Outbound Q status */
+#define    RC_CMD_OUTBOUND_INIT_IN_PROGRESS          0x01
+#define    RC_CMD_OUTBOUND_INIT_REJECTED             0x02
+#define    RC_CMD_OUTBOUND_INIT_FAILED               0x03
+#define    RC_CMD_OUTBOUND_INIT_COMPLETE             0x04
+
+
+#define    UTIL_NOP                                0x00
+
+
+/* RC Get Status State values */
+
+#define    ADAPTER_STATE_INITIALIZING                  0x01
+#define    ADAPTER_STATE_RESET                         0x02
+#define    ADAPTER_STATE_HOLD                          0x04
+#define    ADAPTER_STATE_READY                         0x05
+#define    ADAPTER_STATE_OPERATIONAL                   0x08
+#define    ADAPTER_STATE_FAILED                        0x10
+#define    ADAPTER_STATE_FAULTED                       0x11
+
+
+/* Defines for Request Status Codes:  Table 3-1 Reply Status Codes.  */
+
+#define    RC_REPLY_STATUS_SUCCESS                    0x00
+#define    RC_REPLY_STATUS_ABORT_DIRTY                0x01
+#define    RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
+#define    RC_REPLY_STATUS_ABORT_PARTIAL_TRANSFER     0x03
+#define    RC_REPLY_STATUS_ERROR_DIRTY                0x04
+#define    RC_REPLY_STATUS_ERROR_NO_DATA_TRANSFER     0x05
+#define    RC_REPLY_STATUS_ERROR_PARTIAL_TRANSFER     0x06
+#define    RC_REPLY_STATUS_PROCESS_ABORT_DIRTY        0x07
+#define    RC_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER   0x08
+#define    RC_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER   0x09
+#define    RC_REPLY_STATUS_TRANSACTION_ERROR          0x0A
+#define    RC_REPLY_STATUS_PROGRESS_REPORT            0x80
+
+
+/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/
+
+#define    RC_DS_SUCCESS                        0x0000
+#define    RC_DS_BAD_KEY                        0x0001
+#define    RC_DS_CHAIN_BUFFER_TOO_LARGE         0x0002
+#define    RC_DS_DEVICE_BUSY                    0x0003
+#define    RC_DS_DEVICE_LOCKED                  0x0004
+#define    RC_DS_DEVICE_NOT_AVAILABLE           0x0005
+#define    RC_DS_DEVICE_RESET                   0x0006
+#define    RC_DS_INAPPROPRIATE_FUNCTION         0x0007
+#define    RC_DS_INSUFFICIENT_RESOURCE_HARD     0x0008
+#define    RC_DS_INSUFFICIENT_RESOURCE_SOFT     0x0009
+#define    RC_DS_INVALID_INITIATOR_ADDRESS      0x000A
+#define    RC_DS_INVALID_MESSAGE_FLAGS          0x000B
+#define    RC_DS_INVALID_OFFSET                 0x000C
+#define    RC_DS_INVALID_PARAMETER              0x000D
+#define    RC_DS_INVALID_REQUEST                0x000E
+#define    RC_DS_INVALID_TARGET_ADDRESS         0x000F
+#define    RC_DS_MESSAGE_TOO_LARGE              0x0010
+#define    RC_DS_MESSAGE_TOO_SMALL              0x0011
+#define    RC_DS_MISSING_PARAMETER              0x0012
+#define    RC_DS_NO_SUCH_PAGE                   0x0013
+#define    RC_DS_REPLY_BUFFER_FULL              0x0014
+#define    RC_DS_TCL_ERROR                      0x0015
+#define    RC_DS_TIMEOUT                        0x0016
+#define    RC_DS_UNKNOWN_ERROR                  0x0017
+#define    RC_DS_UNKNOWN_FUNCTION               0x0018
+#define    RC_DS_UNSUPPORTED_FUNCTION           0x0019
+#define    RC_DS_UNSUPPORTED_VERSION            0x001A
+
+ /* msg header defines for VersionOffset */
+#define RCMSGVER_1   0x0001
+#define SGL_OFFSET_0    RCMSGVER_1
+#define SGL_OFFSET_4    (0x0040 | RCMSGVER_1)
+#define TRL_OFFSET_5    (0x0050 | RCMSGVER_1)
+#define TRL_OFFSET_6    (0x0060 | RCMSGVER_1)
+
+ /* msg header defines for MsgFlags */
+#define MSG_STATIC      0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL        0x2000
+#define MSG_LAST        0x4000
+#define MSG_REPLY       0x8000
+
+  /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
+#define LAN_MSG_REQST  (MSG_MULTI_TRANS | SGL_OFFSET_4)
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE  0x00040000
+#define FIVE_WORD_MSG_SIZE  0x00050000
+#define SIX_WORD_MSG_SIZE   0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE  0x00090000
+
+/* Special TID Assignments */
+
+#define ADAPTER_TID   0
+#define HOST_TID  1
+
+ /* RedCreek private message codes */
+#define RC_PRIVATE_GET_MAC_ADDR     0x0001/**/ /* OBSOLETE */
+#define RC_PRIVATE_SET_MAC_ADDR     0x0002
+#define RC_PRIVATE_GET_LAN_STATS    0x0003
+#define RC_PRIVATE_GET_LINK_STATUS  0x0004
+#define RC_PRIVATE_SET_LINK_SPEED   0x0005
+#define RC_PRIVATE_SET_IP_AND_MASK  0x0006
+/* #define RC_PRIVATE_GET_IP_AND_MASK  0x0007 */ /* OBSOLETE */
+#define RC_PRIVATE_GET_LINK_SPEED   0x0008
+#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009
+/* #define RC_PRIVATE_GET_MAC_ADDR     0x000A *//**/
+#define RC_PRIVATE_GET_IP_AND_MASK  0x000B /**/
+#define RC_PRIVATE_DEBUG_MSG        0x000C
+#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY  0x000D
+
+#define RC_PRIVATE_REBOOT           0x00FF
+
+
+/* RC message header */
+typedef struct _RC_MSG_FRAME 
+{
+   U8                          VersionOffset;
+   U8                          MsgFlags;
+   U16                         MessageSize;
+   BF                          TargetAddress:TID_SZ;
+   BF                          InitiatorAddress:TID_SZ;
+   BF                          Function:FUNCTION_SZ;
+   U32                         InitiatorContext;
+   /* SGL[] */ 
+}
+ RC_MSG_FRAME, *PRC_MSG_FRAME;
+
+
+ /* assumed a 16K minus 256 byte space for outbound queue message frames */
+#define MSG_FRAME_SIZE  512
+#define NMBR_MSG_FRAMES 30
+
+/*
+**  Message Unit CSR definitions for RedCreek PCI45 board
+*/
+typedef struct tag_rcatu 
+{
+    volatile unsigned long APICRegSel;  /* APIC Register Select */
+    volatile unsigned long reserved0;
+    volatile unsigned long APICWinReg;  /* APIC Window Register */
+    volatile unsigned long reserved1;
+    volatile unsigned long InMsgReg0;   /* inbound message register 0 */
+    volatile unsigned long InMsgReg1;   /* inbound message register 1 */
+    volatile unsigned long OutMsgReg0;  /* outbound message register 0 */
+    volatile unsigned long OutMsgReg1;  /* outbound message register 1 */
+    volatile unsigned long InDoorReg;   /* inbound doorbell register */
+    volatile unsigned long InIntStat;   /* inbound interrupt status register */
+    volatile unsigned long InIntMask;   /* inbound interrupt mask register */
+    volatile unsigned long OutDoorReg;  /* outbound doorbell register */
+    volatile unsigned long OutIntStat;  /* outbound interrupt status register */
+    volatile unsigned long OutIntMask;  /* outbound interrupt mask register */
+    volatile unsigned long reserved2;
+    volatile unsigned long reserved3;
+    volatile unsigned long InQueue;     /* inbound queue port */
+    volatile unsigned long OutQueue;    /* outbound queue port */
+    volatile unsigned long reserved4;
+    volatile unsigned long reserver5;
+     /* RedCreek extension */
+    volatile unsigned long EtherMacLow;
+    volatile unsigned long EtherMacHi;
+    volatile unsigned long IPaddr;
+    volatile unsigned long IPmask;
+}
+ ATU, *PATU;
+
+ /* 
+ ** typedef PAB
+ **
+ ** PCI Adapter Block - holds instance specific information and is located
+ ** in a reserved space at the start of the message buffer allocated by user.
+ */
+typedef struct
+{
+    PATU             p_atu;                /* ptr to  ATU register block */
+    PU8              pPci45LinBaseAddr;
+    PU8              pLinOutMsgBlock;
+    U32              outMsgBlockPhyAddr; 
+    PFNTXCALLBACK    pTransCallbackFunc;
+    PFNRXCALLBACK    pRecvCallbackFunc;
+    PFNCALLBACK      pRebootCallbackFunc;
+    PFNCALLBACK      pCallbackFunc;
+    U16              ADAPTERState;
+    U16              InboundMFrameSize;
+}
+ PAB, *PPAB;
+
+ /* 
+ ** in reserved space right after PAB in host memory is area for returning
+ ** values from card 
+ */
+
+ /* 
+ ** Array of pointers to PCI Adapter Blocks.
+ ** Indexed by a zero based (0-31) interface number.
+ */ 
+#define MAX_ADAPTERS 32
+static PPAB  PCIAdapterBlock[MAX_ADAPTERS] = 
+{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+
+/*
+** typedef NICSTAT
+**
+** Data structure for NIC statistics retruned from PCI card.  Data copied from
+** here to user allocated RCLINKSTATS (see rclanmtl.h) structure.
+*/
+typedef struct tag_NicStat 
+{
+        unsigned long   TX_good; 
+        unsigned long   TX_maxcol;
+        unsigned long   TX_latecol;
+        unsigned long   TX_urun;
+        unsigned long   TX_crs;         /* lost carrier sense */
+        unsigned long   TX_def;         /* transmit deferred */
+        unsigned long   TX_singlecol;   /* single collisions */
+        unsigned long   TX_multcol;
+        unsigned long   TX_totcol;
+        unsigned long   Rcv_good;
+        unsigned long   Rcv_CRCerr;
+        unsigned long   Rcv_alignerr;
+        unsigned long   Rcv_reserr;     /* rnr'd pkts */
+        unsigned long   Rcv_orun;
+        unsigned long   Rcv_cdt;
+        unsigned long   Rcv_runt;
+        unsigned long   dump_status;    /* last field directly from the chip */
+} 
+ NICSTAT, *P_NICSTAT;
+ 
+
+#define DUMP_DONE   0x0000A005      /* completed statistical dump */
+#define DUMP_CLEAR  0x0000A007      /* completed stat dump and clear counters */
+
+
+static volatile int msgFlag;
+
+
+/* local function prototypes */
+static void ProcessOutboundAdapterMsg(PPAB pPab, U32 phyMsgAddr);
+static int FillAdapterMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
+static int GetAdapterStatus(PPAB pPab);
+static int SendAdapterOutboundQInitMsg(PPAB pPab);
+static int SendEnableSysMsg(PPAB pPab);
+
+
+ /* 1st 100h bytes of message block is reserved for messenger instance */
+#define ADAPTER_BLOCK_RESERVED_SPACE 0x100
+
+/*
+** =========================================================================
+** InitRCApiMsgLayer()
+**
+** Initialize the RedCreek API Module and adapter.
+**
+** Inputs:  AdapterID - interface number from 0 to 15
+**          pciBaseAddr - virual base address of PCI (set by BIOS)
+**          p_msgbuf - virual address to private message block (min. 16K)
+**          p_phymsgbuf - physical address of private message block
+**          TransmitCallbackFunction - address of transmit callback function
+**          ReceiveCallbackFunction  - address of receive  callback function
+**
+** private message block is allocated by user.  It must be in locked pages.
+** p_msgbuf and p_phymsgbuf point to the same location.  Must be contigous
+** memory block of a minimum of 16K byte and long word aligned.
+** =========================================================================
+*/
+RC_RETURN
+InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
+                  PU8 p_msgbuf,  PU8 p_phymsgbuf,
+                  PFNTXCALLBACK  TransmitCallbackFunction,
+                  PFNRXCALLBACK  ReceiveCallbackFunction,
+                  PFNCALLBACK    RebootCallbackFunction)
+{
+     int result;
+     PPAB pPab; 
+    
+#ifdef DEBUG
+     kprintf("InitAPI: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n"
+             "TransmitCallbackFunction:0x%08.8ulx  ReceiveCallbackFunction:0x%08.8ulx\n",
+             AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction);
+#endif /* DEBUG */
+
+    
+     /* Check if this interface already initialized - if so, shut it down */
+     if (PCIAdapterBlock[AdapterID] != NULL)
+     {
+          printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
+//        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+          PCIAdapterBlock[AdapterID] = NULL;
+     }
+
+    /* 
+    ** store adapter instance values in adapter block.
+    ** Adapter block is at beginning of message buffer
+    */  
+    pPab = (PPAB)p_msgbuf;
+    
+    pPab->p_atu = (PATU)pciBaseAddr;
+    pPab->pPci45LinBaseAddr =  (PU8)pciBaseAddr;
+    
+     /* Set outbound message frame addr - skip over Adapter Block */
+    pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+    pPab->pLinOutMsgBlock    = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
+
+     /* store callback function addresses */
+    pPab->pTransCallbackFunc = TransmitCallbackFunction;
+    pPab->pRecvCallbackFunc  = ReceiveCallbackFunction;
+    pPab->pRebootCallbackFunc  = RebootCallbackFunction;
+    pPab->pCallbackFunc  = (PFNCALLBACK)NULL;
+
+    /*
+    ** Initialize API 
+    */
+    result = GetAdapterStatus(pPab);
+    
+    if (result != RC_RTN_NO_ERROR)
+        return result;
+
+    if (pPab->ADAPTERState == ADAPTER_STATE_OPERATIONAL)
+    {
+         printk("pPab->ADAPTERState == op: resetting adapter\n");
+         RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
+    }
+        
+    result = SendAdapterOutboundQInitMsg(pPab);
+    
+    if (result != RC_RTN_NO_ERROR)
+         return result;
+
+    result = SendEnableSysMsg(pPab);
+   
+    if (result != RC_RTN_NO_ERROR)
+         return result;
+   
+    PCIAdapterBlock[AdapterID] = pPab;
+    return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** Disable and Enable Adapter interrupts.  Adapter interrupts are enabled at Init time
+** but can be disabled and re-enabled through these two function calls.
+** Packets will still be put into any posted received buffers and packets will
+** be sent through RCSendPacket() functions.  Disabling Adapter interrupts
+** will prevent hardware interrupt to host even though the outbound Adapter msg
+** queue is not emtpy.
+** =========================================================================
+*/
+#define i960_OUT_POST_Q_INT_BIT        0x0008 /* bit set masks interrupts */
+
+RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID)
+{
+    PPAB pPab;
+
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+        
+    pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT;
+
+    return RC_RTN_NO_ERROR;
+}
+
+RC_RETURN RCEnableAdapterInterrupts(U16 AdapterID)
+{
+    PPAB pPab;
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+         return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT;
+    
+    return RC_RTN_NO_ERROR;
+
+}
+
+
+/*
+** =========================================================================
+** RCSendPacket()
+** =========================================================================
+*/
+RC_RETURN
+RCSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
+{
+    U32 msgOffset;
+    PU32 pMsg;
+    int size;
+    PPAB pPab;
+
+#ifdef DEBUG
+kprintf("RCSendPacket()...\n");
+#endif /* DEBUG */
+    
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+     /* get Inbound free Q entry - reading from In Q gets free Q entry */
+     /* offset to Msg Frame in PCI msg block */
+
+    msgOffset = pPab->p_atu->InQueue; 
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("RCSendPacket(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+        
+     /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+    if (size == -1) /* error processing TCB - send NOP msg */
+    {
+#ifdef DEBUG
+            kprintf("RCSendPacket(): Error Rrocess TCB!\n");
+#endif /* DEBUG */
+        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+        pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+        return RC_RTN_TCB_ERROR;
+    }
+    else /* send over msg header */
+    {    
+        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+        pMsg[1] = LAN_PACKET_SEND << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+        pMsg[2] = InitiatorContext;
+        pMsg[3] = 0;  /* batch reply */
+         /* post to Inbound Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_NO_ERROR;
+    }
+}
+ 
+
+/*
+** =========================================================================
+** RCPostRecvBuffer()
+**
+** inputs:  pBufrCntrlBlock - pointer to buffer control block
+**
+** returns TRUE if successful in sending message, else FALSE.
+** =========================================================================
+*/
+RC_RETURN
+RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
+{
+    U32 msgOffset;
+    PU32 pMsg;
+    int size;
+    PPAB pPab;
+
+#ifdef DEBUG
+kprintf("RCPostRecvBuffers()...\n");
+#endif /* DEBUG */
+    
+     /* search for DeviceHandle */
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+
+     /* get Inbound free Q entry - reading from In Q gets free Q entry */
+     /* offset to Msg Frame in PCI msg block */
+    msgOffset = pPab->p_atu->InQueue; 
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+            kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+   
+    }
+     /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    size = FillAdapterMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);
+
+    if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */
+    {
+#ifdef DEBUG
+        kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size);
+#endif /* DEBUG */
+        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
+        pMsg[1] = UTIL_NOP << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+         /* post to Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_TCB_ERROR;
+    }
+    else /* send over size msg header */
+    {    
+        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
+        pMsg[1] = LAN_RECEIVE_POST << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+        pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+        pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
+         /* post to Post Q */   
+        pPab->p_atu->InQueue = msgOffset;
+        return RC_RTN_NO_ERROR;
+    }
+}
+
+
+/*
+** =========================================================================
+** RCProcMsgQ()
+**
+** Process outbound message queue until empty.
+** =========================================================================
+*/
+void 
+RCProcMsgQ(U16 AdapterID)
+{
+    U32 phyAddrMsg;
+    PU8 p8Msg;
+    PU32 p32;
+    U16 count;
+    PPAB pPab;
+    unsigned char debug_msg[20];
+    
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return;
+    
+    phyAddrMsg = pPab->p_atu->OutQueue;
+
+    while (phyAddrMsg != 0xFFFFFFFF)
+    {
+        p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+        p32 = (PU32)p8Msg;
+        
+        //printk(" msg: 0x%x  0x%x \n", p8Msg[7], p32[5]);
+
+     /* 
+     ** Send Packet Reply Msg
+     */
+        if (LAN_PACKET_SEND == p8Msg[7])  /* function code byte */
+        {
+            count = *(PU16)(p8Msg+2);
+            count -= p8Msg[0] >> 4;
+                                      /* status, count, context[], adapter */
+           (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
+        }             
+     /* 
+     ** Receive Packet Reply Msg */
+        else if (LAN_RECEIVE_POST == p8Msg[7])
+        {
+#ifdef DEBUG    
+    kprintf("RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32);
+    kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+            p32[0], p32[1], p32[2], p32[3]);
+    kprintf("     0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+            p32[4], p32[5], p32[6], p32[7]);
+    kprintf("     0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+            p32[8], p32[9], p32[10], p32[11]);
+#endif
+               /*  status, count, buckets remaining, packetParmBlock, adapter */
+          (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID);
+      
+      
+        }
+        else if (LAN_RESET == p8Msg[7] || LAN_SHUTDOWN == p8Msg[7])
+        {
+            if (pPab->pCallbackFunc)
+            {
+                (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID);
+            }
+            else
+            {
+                pPab->pCallbackFunc = (PFNCALLBACK) 1;
+            }
+            //PCIAdapterBlock[AdapterID] = 0;
+        }
+        else if (RC_PRIVATE == p8Msg[7])
+        {
+            //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]);
+          switch (p32[5])
+          {
+          case RC_PRIVATE_DEBUG_MSG:
+               msgFlag = 1;
+               /*printk("Received RC_PRIVATE msg\n");*/
+               debug_msg[15]  = (p32[6]&0xff000000) >> 24;
+               debug_msg[14]  = (p32[6]&0x00ff0000) >> 16;
+               debug_msg[13]  = (p32[6]&0x0000ff00) >> 8;
+               debug_msg[12]  = (p32[6]&0x000000ff);
+
+               debug_msg[11]  = (p32[7]&0xff000000) >> 24;
+               debug_msg[10]  = (p32[7]&0x00ff0000) >> 16;
+               debug_msg[ 9]  = (p32[7]&0x0000ff00) >> 8;
+               debug_msg[ 8]  = (p32[7]&0x000000ff);
+
+               debug_msg[ 7]  = (p32[8]&0xff000000) >> 24;
+               debug_msg[ 6]  = (p32[8]&0x00ff0000) >> 16;
+               debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8;
+               debug_msg[ 4] = (p32[8]&0x000000ff);
+
+               debug_msg[ 3] = (p32[9]&0xff000000) >> 24;
+               debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16;
+               debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8;
+               debug_msg[ 0] = (p32[9]&0x000000ff);
+
+               debug_msg[16] = '\0';
+               printk (debug_msg);
+               break;
+          case RC_PRIVATE_REBOOT:
+               printk("Adapter reboot initiated...\n");
+               if (pPab->pRebootCallbackFunc)
+               {
+                    (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID);
+               }
+               break;
+          default:
+                printk("Unknown private msg received: 0x%x\n",
+                      p32[5]);
+               break;
+          }
+        }
+
+     /* 
+     ** Process other Msg's
+     */
+        else
+        {
+            ProcessOutboundAdapterMsg(pPab, phyAddrMsg);
+        }
+        
+         /* return MFA to outbound free Q*/
+        pPab->p_atu->OutQueue = phyAddrMsg;
+    
+         /* any more msgs? */
+        phyAddrMsg = pPab->p_atu->OutQueue;
+    }
+}
+
+
+/*
+** =========================================================================
+**  Returns LAN interface statistical counters to space provided by caller at
+**  StatsReturnAddr.  Returns 0 if success, else RC_RETURN code.
+**  This function will call the WaitCallback function provided by
+**  user while waiting for card to respond.
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatistics(U16 AdapterID, 
+                    P_RCLINKSTATS StatsReturnAddr,
+                    PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset;
+    volatile U32 timeout;
+    volatile PU32 pMsg;
+    volatile PU32 p32, pReturnAddr;
+    P_NICSTAT pStats;
+    int i;
+    PPAB pPab;
+
+/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+    #ifdef DEBUG
+        kprintf("Get8255XStats(): Inbound Free Q empty!\n");
+    #endif
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+     /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x112; /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LAN_STATS;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+
+    pStats = (P_NICSTAT)p32;
+    pStats->dump_status = 0xFFFFFFFF;
+
+     /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    timeout = 100000;
+    while (1)
+    {
+        if (WaitCallback)
+                (*WaitCallback)();
+        
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (pStats->dump_status != 0xFFFFFFFF)
+            break;
+
+        if (!timeout--)
+        {
+        #ifdef DEBUG
+            kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n");
+        #endif
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    pReturnAddr = (PU32)StatsReturnAddr;
+    
+     /* copy Nic stats to user's structure */
+    for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
+        pReturnAddr[i] = p32[i];
+   
+    return RC_RTN_NO_ERROR;     
+}
+
+
+/*
+** =========================================================================
+** Get82558LinkStatus()
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
+{
+    U32 msgOffset;
+    volatile U32 timeout;
+    volatile PU32 pMsg;
+    volatile PU32 p32;
+    PPAB pPab;
+
+/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+    #ifdef DEBUG
+        dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n");
+    #endif
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+     /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
+
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x112; /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    *p32 = 0xFFFFFFFF;
+    
+     /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    timeout = 100000;
+    while (1)
+    {
+        U32 i;
+
+        if (WaitCallback)
+                (*WaitCallback)();
+        
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (*p32 != 0xFFFFFFFF)
+            break;
+
+        if (!timeout--)
+        {
+        #ifdef DEBUG
+            kprintf("Timeout waiting for link status\n");
+        #endif    
+            return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+    
+    *ReturnAddr = *p32; /* 1 = up 0 = down */
+    
+    return RC_RTN_NO_ERROR;
+    
+}
+
+/*
+** =========================================================================
+** RCGetMAC()
+**
+** get the MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
+{
+     unsigned i, timeout;
+     U32      off;
+     PU32     p;
+     U32      temp[2];
+     PPAB     pPab;
+     PATU     p_atu;
+    
+     pPab = PCIAdapterBlock[AdapterID];
+    
+     if (pPab == NULL)
+          return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+     p_atu = pPab->p_atu;
+
+     p_atu->EtherMacLow = 0;     /* first zero return data */
+     p_atu->EtherMacHi = 0;
+    
+     off = p_atu->InQueue;   /* get addresss of message */
+ 
+     if (0xFFFFFFFF == off)
+          return RC_RTN_FREE_Q_EMPTY;
+
+     p = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef RCDEBUG
+     printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
+            (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+     /* setup private message */
+     p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+     p[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+     p[2] = 0;               /* initiator context */
+     p[3] = 0x218;           /* transaction context */
+     p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR;
+
+
+     p_atu->InQueue = off;   /* send it to the device */
+#ifdef RCDEBUG
+     printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
+            (uint)p_atu, (uint)off, (uint)p);
+#endif /* RCDEBUG */
+
+     /* wait for the rcpci45 board to update the info */
+     timeout = 1000000;
+     while (0 == p_atu->EtherMacLow) 
+     {
+          if (WaitCallback)
+               (*WaitCallback)();
+    
+          for (i = 0; i < 1000; i++)
+               ;
+
+          if (!timeout--)
+          {
+               printk("rc_getmac: Timeout\n");
+               return RC_RTN_MSG_REPLY_TIMEOUT;
+          }
+     }
+    
+     /* read the mac address  */
+     temp[0] = p_atu->EtherMacLow;
+     temp[1] = p_atu->EtherMacHi;
+     memcpy((char *)mac, (char *)temp, 6);
+
+
+#ifdef RCDEBUG
+//    printk("rc_getmac: 0x%X\n", ptr);
+#endif /* RCDEBUG */
+
+     return RC_RTN_NO_ERROR;
+}
+
+ 
+/*
+** =========================================================================
+** RCSetMAC()
+**
+** set MAC address the adapter is listening for in non-promiscous mode.
+** MAC address is in media format.
+** =========================================================================
+*/
+RC_RETURN
+RCSetMAC(U16 AdapterID, PU8 mac)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+         return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR;
+    pMsg[5] = *(unsigned *)mac;  /* first four bytes */
+    pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */
+    
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetLinkSpeed()
+**
+** set ethernet link speed. 
+** input: speedControl - determines action to take as follows
+**          0 = reset and auto-negotiate (NWay)
+**          1 = Full Duplex 100BaseT
+**          2 = Half duplex 100BaseT
+**          3 = Full Duplex  10BaseT
+**          4 = Half duplex  10BaseT
+**          all other values are ignore (do nothing)
+** =========================================================================
+*/
+RC_RETURN
+RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+         return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED;
+    pMsg[5] = LinkSpeedCode;     /* link speed code */
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    return RC_RTN_NO_ERROR ;
+}
+
+/*
+** =========================================================================
+** RCGetLinkSpeed()
+**
+** get ethernet link speed. 
+**
+** 0 = Unknown
+** 1 = Full Duplex 100BaseT
+** 2 = Half duplex 100BaseT
+** 3 = Full Duplex  10BaseT
+** 4 = Half duplex  10BaseT
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
+{
+     U32 msgOffset, timeout;
+     PU32 pMsg;
+     volatile PU32 p32;
+     U8 AdapterLinkSpeed;
+     PPAB pPab;
+
+     pPab =PCIAdapterBlock[AdapterID];
+
+
+     msgOffset = pPab->p_atu->InQueue;
+
+
+     if (msgOffset == 0xFFFFFFFF)
+     {
+          kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
+          return RC_RTN_FREE_Q_EMPTY;
+     }
+
+     /* calc virtual address of msg - virtual already mapped to physical */    
+     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+     /* virtual pointer to return buffer - clear first two dwords */
+     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+     p32[0] = 0xff;
+
+     /* setup private message */
+     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+     pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+     pMsg[2] = 0;                 /* initiator context */
+     pMsg[3] = 0x219;             /* transaction context */
+     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED;
+     /* phys address to return status - area right after PAB */
+     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+   
+     /* post to Inbound Post Q */   
+
+     pPab->p_atu->InQueue = msgOffset;
+
+     /* wait for response */
+     timeout = 1000000;
+     while(1)
+     {
+          int i;
+        
+          if (WaitCallback)
+                (*WaitCallback)();
+
+          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+               ;
+            
+          if (p32[0] != 0xff)
+               break;
+            
+          if (!timeout--)
+          {
+               kprintf("Timeout waiting for link speed from adapter\n");
+               kprintf("0x%08.8ulx\n", p32[0]);
+               return RC_RTN_NO_LINK_SPEED;
+          }
+     }
+
+     /* get Link speed */
+     AdapterLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;
+
+     *pLinkSpeedCode= AdapterLinkSpeed;
+
+     return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE   0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability)
+{
+    U32  off;
+    PU32 pMsg;
+    PPAB pPab;
+
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+         return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY;
+    pMsg[5] = capability;
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    return RC_RTN_NO_ERROR ;
+}
+
+/*
+** =========================================================================
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** =========================================================================
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
+{
+     U32 msgOffset, timeout;
+     PU32 pMsg;
+     volatile PU32 p32;
+     PPAB pPab;
+
+     pPab =PCIAdapterBlock[AdapterID];
+
+     msgOffset = pPab->p_atu->InQueue;
+
+
+     if (msgOffset == 0xFFFFFFFF)
+     {
+          kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n");
+          return RC_RTN_FREE_Q_EMPTY;
+     }
+
+     /* calc virtual address of msg - virtual already mapped to physical */    
+     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+     /* virtual pointer to return buffer - clear first two dwords */
+     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+     p32[0] = 0xff;
+
+     /* setup private message */
+     pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
+     pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+     pMsg[2] = 0;                 /* initiator context */
+     pMsg[3] = 0x219;             /* transaction context */
+     pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV;
+     /* phys address to return status - area right after PAB */
+     pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+
+   
+     /* post to Inbound Post Q */   
+
+     pPab->p_atu->InQueue = msgOffset;
+
+    
+     /* wait for response */
+     timeout = 1000000;
+     while(1)
+     {
+          int i;
+        
+          if (WaitCallback)
+                (*WaitCallback)();
+            
+          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+               ;
+
+          if (p32[0] != 0xff)
+               break;
+            
+          if (!timeout--)
+          {
+               kprintf("Timeout waiting for link speed from adapter\n");
+               return RC_RTN_NO_FIRM_VER;
+          }
+     }
+
+     strcpy(pFirmString, (PU8)p32);
+     return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCResetLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when 
+** reset is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** reset is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+    unsigned long off;
+    unsigned long *pMsg;
+    PPAB pPab;
+    int i;
+    long timeout = 0;
+
+    
+    pPab =PCIAdapterBlock[AdapterID];
+     
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+         return RC_RTN_FREE_Q_EMPTY;
+    
+    pPab->pCallbackFunc = CallbackFunction;
+
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup message */
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = LAN_RESET << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = ResourceFlags << 16;   /* resource flags */
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    if (CallbackFunction == (PFNCALLBACK)NULL)
+    {
+        /* call RCProcMsgQ() until something in pPab->pCallbackFunc
+           or until timer goes off */
+        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+        {
+            RCProcMsgQ(AdapterID);
+            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
+               ;
+            timeout++;
+            if (timeout > 10000)
+            {
+                break;
+            }
+        }
+        if (ReturnAddr != (PU32)NULL)
+           *ReturnAddr = (U32)pPab->pCallbackFunc;
+    }
+
+    return RC_RTN_NO_ERROR ;
+}
+/*
+** =========================================================================
+** RCResetAdapter()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCResetAdapter(U16 AdapterID)
+{
+     U32 msgOffset, timeout;
+     PU32 pMsg;
+     PPAB pPab;
+     volatile PU32 p32;
+    
+     pPab = PCIAdapterBlock[AdapterID];
+     msgOffset = pPab->p_atu->InQueue;
+
+     if (msgOffset == 0xFFFFFFFF)
+     {
+          return RC_RTN_FREE_Q_EMPTY;
+     }
+
+     /* calc virtual address of msg - virtual already mapped to physical */    
+     pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+     pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+     pMsg[1] = RC_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID;
+     pMsg[2] = 0; /* universal context */
+     pMsg[3] = 0; /* universal context */
+     pMsg[4] = 0; /* universal context */
+     pMsg[5] = 0; /* universal context */
+     /* phys address to return status - area right after PAB */
+     pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+     pMsg[7] = 0;
+     pMsg[8] = 1;  /*  return 1 byte */
+
+     /* virual pointer to return buffer - clear first two dwords */
+     p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+     p32[0] = 0;
+     p32[1] = 0;
+
+     /* post to Inbound Post Q */   
+
+     pPab->p_atu->InQueue = msgOffset;
+
+     /* wait for response */
+     timeout = 1000000;
+     while(1)
+     {
+          int i;
+        
+          for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+               ;
+            
+          if (p32[0] || p32[1])
+               break;
+            
+          if (!timeout--)
+          {
+               printk("RCResetAdapter timeout\n");
+               return RC_RTN_MSG_REPLY_TIMEOUT;
+          }
+     }
+     return RC_RTN_NO_ERROR;
+}
+
+/*
+** =========================================================================
+** RCShutdownLANCard()
+**
+** ResourceFlags indicates whether to return buffer resource explicitly
+** to host or keep and reuse.
+** CallbackFunction (if not NULL) is the function to be called when 
+** shutdown is complete.
+** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
+** shutdown is done (if not NULL).
+**
+** =========================================================================
+*/
+RC_RETURN 
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
+{
+    volatile PU32 pMsg;
+    U32 off;
+    PPAB pPab;
+    int i;
+    long timeout = 0;
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+         return RC_RTN_FREE_Q_EMPTY;
+
+    pPab->pCallbackFunc = CallbackFunction;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup message */
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = LAN_SHUTDOWN << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = ResourceFlags << 16;   /* resource flags */
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+
+    if (CallbackFunction == (PFNCALLBACK)NULL)
+    {
+        /* call RCProcMsgQ() until something in pPab->pCallbackFunc
+           or until timer goes off */
+        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
+        {
+            RCProcMsgQ(AdapterID);
+            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
+               ;
+            timeout++;
+            if (timeout > 10000)
+            {
+                break;
+            }
+        }
+        if (ReturnAddr != (PU32)NULL)
+           *ReturnAddr = (U32)pPab->pCallbackFunc;
+    }
+    return RC_RTN_NO_ERROR ;
+}
+
+
+/*
+** =========================================================================
+** RCSetRavlinIPandMask()
+**
+** Set the Ravlin 45/PCI cards IP address and network mask.
+**
+** IP address and mask must be in network byte order.
+** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+** 0x04030201 and 0x00FFFFFF on a little endian machine.
+**
+** =========================================================================
+*/
+RC_RETURN
+RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
+{
+    volatile PU32 pMsg;
+    U32 off;
+    PPAB pPab;
+
+    pPab = PCIAdapterBlock[AdapterID];
+
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+
+    off = pPab->p_atu->InQueue; /* get addresss of message */
+    
+    if (0xFFFFFFFF == off)
+         return RC_RTN_FREE_Q_EMPTY;
+    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+    /* setup private message */
+    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = 0;                 /* initiator context */
+    pMsg[3] = 0x219;             /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK;
+    pMsg[5] = ipAddr; 
+    pMsg[6] = netMask;
+
+
+    pPab->p_atu->InQueue = off;   /* send it to the device */
+    return RC_RTN_NO_ERROR ;
+
+}
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+** 
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+                        PFNWAITCALLBACK WaitCallback)
+{
+    unsigned i, timeout;
+    U32      off;
+    PU32     pMsg, p32;
+    PPAB     pPab;
+    PATU     p_atu;
+    
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+    pPab = PCIAdapterBlock[AdapterID];
+    
+    if (pPab == NULL)
+        return RC_RTN_ADPTR_NOT_REGISTERED;
+    
+    p_atu = pPab->p_atu;
+    off = p_atu->InQueue;   /* get addresss of message */
+ 
+   if (0xFFFFFFFF == off)
+         return RC_RTN_FREE_Q_EMPTY;
+
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    *p32 = 0xFFFFFFFF;
+
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+    /* setup private message */
+    pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_PRIVATE << 24 | HOST_TID << 12 | LAN_TARGET_ID;
+    pMsg[2] = 0;               /* initiator context */
+    pMsg[3] = 0x218;           /* transaction context */
+    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK;
+    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+    p_atu->InQueue = off;   /* send it to the device */
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
+#endif /* DEBUG */
+
+     /* wait for the rcpci45 board to update the info */
+    timeout = 100000;
+    while (0xffffffff == *p32)
+    {
+        if (WaitCallback)
+                (*WaitCallback)();
+    
+        for (i = 0; i < 1000; i++)
+            ;
+
+        if (!timeout--)
+        {
+        #ifdef DEBUG
+            kprintf("RCGetRavlinIPandMask: Timeout\n");
+        #endif /* DEBUG */
+             return RC_RTN_MSG_REPLY_TIMEOUT;
+        }
+    }
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: after time out\n", \
+            "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]);
+#endif /* DEBUG */
+    
+    /* send IP and mask to user's space  */
+    *pIpAddr  = p32[0];
+    *pNetMask = p32[1];
+
+
+#ifdef DEBUG
+    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
+#endif /* DEBUG */
+
+    return RC_RTN_NO_ERROR;
+}
+
+/* 
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+**
+**                        local functions
+**
+** /////////////////////////////////////////////////////////////////////////
+** /////////////////////////////////////////////////////////////////////////
+*/
+
+/*
+** =========================================================================
+** SendAdapterOutboundQInitMsg()
+**
+** =========================================================================
+*/
+static int 
+SendAdapterOutboundQInitMsg(PPAB pPab)
+{
+    U32 msgOffset, timeout, phyOutQFrames, i;
+    volatile PU32 pMsg;
+    volatile PU32 p32;
+    
+    
+    
+    msgOffset = pPab->p_atu->InQueue;
+
+    
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("SendAdapterOutboundQInitMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+    
+    
+     /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+kprintf("SendAdapterOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+    pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
+    pMsg[1] = RC_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x106; /* transaction context */
+    pMsg[4] = 4096; /* Host page frame size */
+    pMsg[5] = MSG_FRAME_SIZE  << 16 | 0x80; /* outbound msg frame size and Initcode */
+    pMsg[6] = 0xD0000004;       /* simple sgl element LE, EOB */
+     /* phys address to return status - area right after PAB */
+    pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+
+     /* virual pointer to return buffer - clear first two dwords */
+    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    
+     /* post to Inbound Post Q */   
+    pPab->p_atu->InQueue = msgOffset;
+    
+  /* wait for response */
+    timeout = 100000;
+    while(1)
+    {
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0])
+            break;
+            
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout wait for InitOutQ InPrgress status from adapter\n");
+#endif /* DEBUG */
+            return RC_RTN_NO_STATUS;
+        }
+    }
+
+    timeout = 100000;
+    while(1)
+    {
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] == RC_CMD_OUTBOUND_INIT_COMPLETE)
+            break;
+
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout wait for InitOutQ Complete status from adapter\n");
+#endif /* DEBUG */
+            return RC_RTN_NO_STATUS;
+        }
+    }
+
+     /* load PCI outbound free Q with MF physical addresses */
+    phyOutQFrames = pPab->outMsgBlockPhyAddr;
+
+    for (i = 0; i < NMBR_MSG_FRAMES; i++)
+    {
+        pPab->p_atu->OutQueue = phyOutQFrames;
+        phyOutQFrames += MSG_FRAME_SIZE;
+    }
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** GetAdapterStatus()
+**
+** Send StatusGet Msg, wait for results return directly to buffer.
+**
+** =========================================================================
+*/
+static int 
+GetAdapterStatus(PPAB pPab)
+{
+    U32 msgOffset, timeout;
+    PU32 pMsg;
+    volatile PU32 p32;
+    
+    
+    msgOffset = pPab->p_atu->InQueue; 
+    printk("GetAdapterStatus: msg offset = 0x%x\n", msgOffset);
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("GetAdapterStatus(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+     /* calc virual address of msg - virual already mapped to physical */    
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID;
+    pMsg[2] = 0; /* universal context */
+    pMsg[3] = 0; /* universal context */
+    pMsg[4] = 0; /* universal context */
+    pMsg[5] = 0; /* universal context */
+     /* phys address to return status - area right after PAB */
+    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
+    pMsg[7] = 0;
+    pMsg[8] = 88;  /*  return 88 bytes */
+
+     /* virual pointer to return buffer - clear first two dwords */
+    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
+    p32[0] = 0;
+    p32[1] = 0;
+
+#ifdef DEBUG
+kprintf("GetAdapterStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n",
+    pMsg, msgOffset, pMsg[1], pMsg[6]);
+#endif /* DEBUG */
+   
+     /* post to Inbound Post Q */   
+    pPab->p_atu->InQueue = msgOffset;
+    
+#ifdef DEBUG
+kprintf("Return status to p32 = 0x%08.8ulx\n", p32);
+#endif /* DEBUG */
+    
+     /* wait for response */
+    timeout = 1000000;
+    while(1)
+    {
+        int i;
+        
+        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
+            ;
+            
+        if (p32[0] && p32[1])
+            break;
+            
+        if (!timeout--)
+        {
+#ifdef DEBUG
+            kprintf("Timeout waiting for status from adapter\n");
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+            return RC_RTN_NO_STATUS;
+        }
+    }
+            
+#ifdef DEBUG
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
+#endif /* DEBUG */
+    /* get adapter state */
+    pPab->ADAPTERState = ((volatile PU8)p32)[10];
+    pPab->InboundMFrameSize  = ((volatile PU16)p32)[6];
+    
+#ifdef DEBUG
+    kprintf("adapter state 0x%02.2x InFrameSize = 0x%04.4x\n", 
+                        pPab->ADAPTERState, pPab->InboundMFrameSize);
+#endif /* DEBUG */
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** SendEnableSysMsg()
+**
+**
+** =========================================================================
+*/
+static int 
+SendEnableSysMsg(PPAB pPab)
+{
+    U32 msgOffset; // timeout;
+    volatile PU32 pMsg;
+
+    msgOffset = pPab->p_atu->InQueue;
+
+    if (msgOffset == 0xFFFFFFFF)
+    {
+#ifdef DEBUG
+        kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n");
+#endif /* DEBUG */
+        return RC_RTN_FREE_Q_EMPTY;
+    }
+
+     /* calc virual address of msg - virual already mapped to physical */
+    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
+
+#ifdef DEBUG
+kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
+#endif /* DEBUG */
+
+    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
+    pMsg[1] = RC_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID;
+    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
+    pMsg[3] = 0x110; /* transaction context */
+    pMsg[4] = 0x50657465; /*  RedCreek Private */
+
+     /* post to Inbound Post Q */
+    pPab->p_atu->InQueue = msgOffset;
+
+    return RC_RTN_NO_ERROR;
+}
+
+
+/*
+** =========================================================================
+** FillI12OMsgFromTCB()
+**
+** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
+**          pXmitCntrlBlock - pointer to caller buffer control block.
+**
+** fills in LAN SGL after Transaction Control Word or Bucket Count.
+** =========================================================================
+*/
+static int 
+FillAdapterMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
+{
+    unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
+    PU32 pTCB, pMsg;
+
+ /* SGL element flags */   
+#define EOB        0x40000000
+#define LE         0x80000000
+#define SIMPLE_SGL 0x10000000
+#define BC_PRESENT 0x01000000
+
+    pTCB = (PU32)pTransCtrlBlock;
+    pMsg = pMsgFrame;
+    nmbrDwords = 0;
+
+#ifdef DEBUG
+ kprintf("FillAdapterMsgSGLFromTCBX\n");
+kprintf("TCB  0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
+               pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
+kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg);
+#endif /* DEBUG */
+    
+    nmbrBuffers = *pTCB++;
+    
+    if (!nmbrBuffers)
+    {
+        return -1;
+    }
+
+    do
+    {
+        context = *pTCB++; /* buffer tag (context) */
+        nmbrSeg = *pTCB++; /* number of segments */
+
+        if (!nmbrSeg)
+        {
+            return -1;
+        }
+        
+        flags = SIMPLE_SGL | BC_PRESENT;
+
+        if (1 == nmbrSeg)
+        {
+            flags |= EOB;
+        
+            if (1 == nmbrBuffers)
+                flags |= LE;
+        }    
+
+         /* 1st SGL buffer element has context */
+        pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */
+        pMsg[1] = context;
+        pMsg[2] = pTCB[1]; /* send buffer segment physical address */
+        nmbrDwords += 3;
+        pMsg += 3;
+        pTCB += 2;
+
+        
+        if (--nmbrSeg)
+        {
+            do
+            {
+                flags = SIMPLE_SGL;
+                
+                if (1 == nmbrSeg)
+                {
+                    flags |= EOB;
+                
+                    if (1 == nmbrBuffers)
+                        flags |= LE;
+                }    
+                
+                pMsg[0] = pTCB[0] | flags;  /* send over count */
+                pMsg[1] = pTCB[1];   /* send buffer segment physical address */
+                nmbrDwords += 2;
+                pTCB += 2;
+                pMsg += 2;
+        
+            } while (--nmbrSeg);
+        }
+        
+    } while (--nmbrBuffers);
+    
+    return nmbrDwords;
+}
+
+
+/*
+** =========================================================================
+** ProcessOutboundAdapterMsg()
+**
+** process reply message
+** * change to msg structure *
+** =========================================================================
+*/
+static void 
+ProcessOutboundAdapterMsg(PPAB pPab, U32 phyAddrMsg)
+{
+    PU8 p8Msg;
+    PU32 p32;
+  //  U16 count;
+    
+    
+    p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
+    p32 = (PU32)p8Msg;
+    
+#ifdef DEBUG
+ kprintf("VXD: ProcessOutboundAdapterMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
+ kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
+ kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
+#endif /* DEBUG */
+
+    if (p32[4] >> 24 != RC_REPLY_STATUS_SUCCESS)
+    {
+#ifdef DEBUG
+        kprintf("Message reply status not success\n");
+#endif /* DEBUG */
+        return;
+    }
+    
+    switch (p8Msg[7] )  /* function code byte */
+    {
+        case RC_CMD_SYS_TAB_SET:
+            msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received RC_CMD_SYS_TAB_SET reply\n");
+#endif /* DEBUG */
+            break;
+
+        case RC_CMD_HRT_GET:
+            msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received RC_CMD_HRT_GET reply\n");
+#endif /* DEBUG */
+            break;
+        
+        case RC_CMD_LCT_NOTIFY:
+            msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received RC_CMD_LCT_NOTIFY reply\n");
+#endif /* DEBUG */
+            break;
+        
+        case RC_CMD_SYS_ENABLE:
+            msgFlag = 1;
+#ifdef DEBUG
+        kprintf("Received RC_CMD_SYS_ENABLE reply\n");
+#endif /* DEBUG */
+            break;
+
+        default:    
+#ifdef DEBUG
+        kprintf("Received UNKNOWN reply\n");
+#endif /* DEBUG */
+            break;
+    }
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcmtl.h linux/drivers/net/rcmtl.h
--- v2.0.35/linux/drivers/net/rcmtl.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rcmtl.h	Sun Nov 15 10:33:03 1998
@@ -0,0 +1,580 @@
+/*
+** *************************************************************************
+**
+**
+**     R C M T L . H             $Revision: 3 $
+**
+**
+**  RedCreek Message Transport Layer header file.
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1997-1998, RedCreek Communications Inc.     ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+**  File Description:
+**
+**  Header file for host message transport layer API and data types.
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** *************************************************************************
+*/
+
+#ifndef RCMTL_H
+#define RCMTL_H
+
+/* Linux specific includes */
+#define kprintf printk
+#ifdef RC_LINUX_MODULE     /* linux modules need non-library version of string functions */
+#include <linux/string.h>
+#else
+#include <string.h>
+#endif
+
+/* PCI/45 Configuration space values */
+#define RC_PCI45_VENDOR_ID  0x4916
+#define RC_PCI45_DEVICE_ID  0x1960
+
+
+ /* RedCreek API function return values */
+#define RC_RTN_NO_ERROR             0
+#define RC_RTN_NOT_INIT             1
+#define RC_RTN_FREE_Q_EMPTY         2
+#define RC_RTN_TCB_ERROR            3
+#define RC_RTN_TRANSACTION_ERROR    4
+#define RC_RTN_ADAPTER_ALREADY_INIT 5
+#define RC_RTN_MALLOC_ERROR         6
+#define RC_RTN_ADPTR_NOT_REGISTERED 7
+#define RC_RTN_MSG_REPLY_TIMEOUT    8
+#define RC_RTN_NO_STATUS            9
+#define RC_RTN_NO_FIRM_VER         10
+#define RC_RTN_NO_LINK_SPEED       11
+
+/* Driver capability flags */
+#define WARM_REBOOT_CAPABLE      0x01
+
+ /* scalar data types */
+typedef unsigned char   U8;
+typedef unsigned char*  PU8;
+typedef unsigned short  U16;
+typedef unsigned short* PU16;
+typedef unsigned long   U32;
+typedef unsigned long*  PU32;
+typedef unsigned long   BF;
+typedef int             RC_RETURN;
+
+
+ /* 
+ ** type PFNWAITCALLBACK
+ **
+ ** pointer to void function - type used for WaitCallback in some functions 
+ */
+typedef void (*PFNWAITCALLBACK)(void);  /* void argument avoids compiler complaint */
+
+ /*
+ ** type PFNTXCALLBACK 
+ **
+ ** Pointer to user's transmit callback function.  This user function is
+ ** called from RCProcMsgQ() when packet have been transmitted from buffers
+ ** given in the RCSendPacket() function.  BufferContext is a pointer to
+ ** an array of 32 bit context values.  These are the values the user assigned
+ ** and passed in the TCB to the RCSendPacket() function.  PcktCount
+ ** indicates the number of buffer context values in the BufferContext[] array.
+ ** The User's TransmitCallbackFunction should recover (put back in free queue)
+ ** the packet buffers associated with the buffer context values.
+ */
+typedef void (*PFNTXCALLBACK)(U32  Status,
+                              U16  PcktCount,
+                              PU32 BufferContext,
+                              U16  AdaterID);
+
+ /* 
+ ** type PFNRXCALLBACK 
+ **
+ ** Pointer to user's receive callback function.  This user function
+ ** is called from RCProcMsgQ() when packets have been received into
+ ** previously posted packet buffers throught the RCPostRecvBuffers() function.
+ ** The received callback function should process the Packet Descriptor Block
+ ** pointed to by PacketDescBlock. See Packet Decription Block below.
+ */
+typedef void (*PFNRXCALLBACK)(U32  Status,
+                              U8   PktCount,
+                              U32  BucketsRemain,
+                              PU32 PacketDescBlock,
+                              U16  AdapterID);
+
+ /* 
+ ** type PFNCALLBACK 
+ **
+ ** Pointer to user's generic callback function.  This user function
+ ** can be passed to LANReset or LANShutdown and is called when the 
+ ** the reset or shutdown is complete.
+ ** Param1 and Param2 are invalid for LANReset and LANShutdown.
+ */
+typedef void (*PFNCALLBACK)(U32  Status,
+                              U32  Param1,
+                              U32  Param2,
+                              U16  AdapterID);
+
+/*
+** Status - Transmit and Receive callback status word 
+**
+** A 32 bit Status is returned to the TX and RX callback functions.  This value
+** contains both the reply status and the detailed status as follows:
+**
+**  32    24     16            0
+**  +------+------+------------+
+**  | Reply|      |  Detailed  |
+**  |Status|   0  |   Status   |
+**  +------+------+------------+
+**
+** Reply Status and Detailed Status of zero indicates No Errors.
+*/
+ /* reply message status defines */
+#define    RC_REPLY_STATUS_SUCCESS                    0x00
+#define    RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
+#define    RC_REPLY_STATUS_TRANSACTION_ERROR          0x0A
+
+
+/* DetailedStatusCode defines */
+#define    RC_DSC_SUCCESS                         0x0000
+#define    RC_DSC_DEVICE_FAILURE                  0x0001
+#define    RC_DSC_DESTINATION_NOT_FOUND           0x0002
+#define    RC_DSC_TRANSMIT_ERROR                  0x0003
+#define    RC_DSC_TRANSMIT_ABORTED                0x0004
+#define    RC_DSC_RECEIVE_ERROR                   0x0005
+#define    RC_DSC_RECEIVE_ABORTED                 0x0006
+#define    RC_DSC_DMA_ERROR                       0x0007
+#define    RC_DSC_BAD_PACKET_DETECTED             0x0008
+#define    RC_DSC_OUT_OF_MEMORY                   0x0009
+#define    RC_DSC_BUCKET_OVERRUN                  0x000A
+#define    RC_DSC_IOP_INTERNAL_ERROR              0x000B
+#define    RC_DSC_CANCELED                        0x000C
+#define    RC_DSC_INVALID_TRANSACTION_CONTEXT     0x000D
+#define    RC_DSC_DESTINATION_ADDRESS_DETECTED    0x000E
+#define    RC_DSC_DESTINATION_ADDRESS_OMITTED     0x000F
+#define    RC_DSC_PARTIAL_PACKET_RETURNED         0x0010
+
+
+/*
+** Packet Description Block   (Received packets)
+**
+** A pointer to this block structure is returned to the ReceiveCallback 
+** function.  It contains the list of packet buffers which have either been
+** filled with a packet or returned to host due to a LANReset function. 
+** Currently there will only be one packet per receive bucket (buffer) posted. 
+**
+**   32   24               0     
+**  +-----------------------+  -\
+**  |   Buffer 1 Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / First Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet 1 length |   / 
+**  +-----------------------+  -\
+**  |   Buffer 2 Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / Second Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet 2 length |   / 
+**  +-----+-----------------+  -
+**  |         ...           |  ----- more bucket descriptors
+**  +-----------------------+  -\
+**  |   Buffer n Context    |    \
+**  +-----------------------+     \
+**  |      0xC0000000       |     / Last Bucket Descriptor
+**  +-----+-----------------+    /
+**  |  0  | packet n length |   / 
+**  +-----+-----------------+  -
+**
+** Buffer Context values are those given to adapter in the TCB on calls to
+** RCPostRecvBuffers().
+**  
+*/
+
+
+
+/*
+** Transaction Control Block (TCB) structure
+**
+** A structure like this is filled in by the user and passed by reference to 
+** RCSendPacket() and RCPostRecvBuffers() functions.  Minimum size is five
+** 32-bit words for one buffer with one segment descriptor.  
+** MAX_NMBR_POST_BUFFERS_PER_MSG defines the maximum single segment buffers
+** that can be described in a given TCB.
+**
+**   32                    0
+**  +-----------------------+
+**  |   Buffer Count        |  Number of buffers in the TCB
+**  +-----------------------+
+**  |   Buffer 1 Context    |  first buffer reference
+**  +-----------------------+
+**  |   Buffer 1 Seg Count  |  number of segments in buffer
+**  +-----------------------+
+**  |   Buffer 1 Seg Desc 1 |  first segment descriptor (size, physical address)
+**  +-----------------------+
+**  |         ...           |  more segment descriptors (size, physical address)
+**  +-----------------------+
+**  |   Buffer 1 Seg Desc n |  last segment descriptor (size, physical address)
+**  +-----------------------+
+**  |   Buffer 2 Context    |  second buffer reference
+**  +-----------------------+
+**  |   Buffer 2 Seg Count  |  number of segments in buffer
+**  +-----------------------+
+**  |   Buffer 2 Seg Desc 1 |  segment descriptor (size, physical address)
+**  +-----------------------+
+**  |         ...           |  more segment descriptors (size, physical address)
+**  +-----------------------+
+**  |   Buffer 2 Seg Desc n |
+**  +-----------------------+
+**  |         ...           |  more buffer descriptor blocks ...
+**  +-----------------------+
+**  |   Buffer n Context    |
+**  +-----------------------+
+**  |   Buffer n Seg Count  |
+**  +-----------------------+
+**  |   Buffer n Seg Desc 1 |
+**  +-----------------------+
+**  |         ...           |
+**  +-----------------------+
+**  |   Buffer n Seg Desc n |
+**  +-----------------------+
+**
+**
+** A TCB for one contigous packet buffer would look like the following:
+**
+**   32                    0
+**  +-----------------------+
+**  |         1             |  one buffer in the TCB
+**  +-----------------------+
+**  |  <user's Context>     |  user's buffer reference
+**  +-----------------------+
+**  |         1             |  one segment buffer
+**  +-----------------------+                            _
+**  |    <buffer size>      |  size                       \ 
+**  +-----------------------+                              \ segment descriptor
+**  |  <physical address>   |  physical address of buffer  /
+**  +-----------------------+                            _/
+**
+*/
+
+ /* Buffer Segment Descriptor */
+typedef struct
+{
+    U32 size;
+    U32 phyAddress;
+}
+ BSD, *PBSD;
+ 
+typedef PU32 PRCTCB;
+/*
+** -------------------------------------------------------------------------
+** Exported functions comprising the API to the message transport layer
+** -------------------------------------------------------------------------
+*/
+
+
+ /*
+ ** InitRCApiMsgLayer()
+ ** 
+ ** Called once prior to using the API message transport layer.  User 
+ ** provides both the physical and virual address of a locked page buffer 
+ ** that is used as a private buffer for the RedCreek API message
+ ** transport layer.  This buffer must be a contigous memory block of a 
+ ** minimum of 16K bytes and long word aligned.  The user also must provide
+ ** the base address of the RedCreek PCI adapter assigned by BIOS or operating
+ ** system.  The user provided value AdapterID is a zero based index of the
+ ** Ravlin 45/PCI adapter.  This interface number is used in all subsequent API
+ ** calls to identify which adpapter for which the function is intended.  
+ ** Up to sixteen interfaces are supported with this API.
+ **
+ ** Inputs:  AdapterID - interface number from 0 to 15
+ **          pciBaseAddr - virual base address of PCI (set by BIOS)
+ **          p_msgbuf - virual address to private message block (min. 16K)
+ **          p_phymsgbuf - physical address of private message block
+ **          TransmitCallbackFunction - address of user's TX callback function
+ **          ReceiveCallbackFunction  - address of user's RX callback function
+ **
+ */
+RC_RETURN InitRCApiMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
+                            PU8 p_msgbuf,  PU8 p_phymsgbuf,
+                            PFNTXCALLBACK TransmitCallbackFunction,
+                            PFNRXCALLBACK ReceiveCallbackFunction,
+                            PFNCALLBACK   RebootCallbackFunction);
+
+ /*
+ ** RCSetRavlinIPandMask()
+ **
+ ** Set the Ravlin 45/PCI cards IP address and network mask.
+ **
+ ** IP address and mask must be in network byte order.
+ ** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
+ ** 0x04030201 and 0x00FFFFFF on a little endian machine.
+ **
+ */
+RC_RETURN RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask);
+
+
+/*
+** =========================================================================
+** RCGetRavlinIPandMask()
+**
+** get the IP address and MASK from the card
+** 
+** =========================================================================
+*/
+RC_RETURN
+RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
+                        PFNWAITCALLBACK WaitCallback);
+
+ /* 
+ ** RCProcMsgQ()
+ ** 
+ ** Called from user's polling loop or Interrupt Service Routine for a PCI 
+ ** interrupt from the RedCreek PCI adapter.  User responsible for determining
+ ** and hooking the PCI interrupt. This function will call the registered
+ ** callback functions, TransmitCallbackFunction or ReceiveCallbackFunction,
+ ** if a TX or RX transaction has completed.
+ */
+void RCProcMsgQ(U16 AdapterID);
+
+
+ /*
+ ** Disable and Enable Adapter interrupts.  Adapter interrupts are enabled at 
+ ** Init time but can be disabled and re-enabled through these two function calls.
+ ** Packets will still be put into any posted recieved buffers and packets will
+ ** be sent through RCSendPacket() functions.  Disabling Adapter interrupts
+ ** will prevent hardware interrupt to host even though the outbound msg
+ ** queue is not emtpy.
+ */
+RC_RETURN RCEnableAdapterInterrupts(U16 adapterID);
+RC_RETURN RCDisableAdapterInterrupts(U16 AdapterID);
+
+
+ /* 
+ ** RCPostRecvBuffers()
+ ** 
+ ** Post user's page locked buffers for use by the PCI adapter to
+ ** return ethernet packets received from the LAN.  Transaction Control Block,
+ ** provided by user, contains buffer descriptor(s) which includes a buffer
+ ** context number along with buffer size and physical address.  See TCB above.
+ ** The buffer context and actual packet length are returned to the 
+ ** ReceiveCallbackFunction when packets have been received.  Buffers posted
+ ** to the RedCreek adapter are considered owned by the adapter until the
+ ** context is return to user through the ReceiveCallbackFunction.
+ */
+RC_RETURN RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransactionCtrlBlock);
+#define MAX_NMBR_POST_BUFFERS_PER_MSG 32
+
+ /*
+ ** RCSendPacket()
+ ** 
+ ** Send user's ethernet packet from a locked page buffer.  
+ ** Packet must have full MAC header, however without a CRC.  
+ ** Initiator context is a user provided value that is returned 
+ ** to the TransmitCallbackFunction when packet buffer is free.
+ ** Transmit buffer are considered owned by the adapter until context's
+ ** returned to user through the TransmitCallbackFunction.
+ */
+RC_RETURN RCSendPacket(U16 AdapterID, 
+                          U32 context, 
+                          PRCTCB pTransactionCtrlBlock);
+
+
+ /* Ethernet Link Statistics structure */
+typedef struct tag_RC_link_stats
+{
+    U32 TX_good;      /* good transmit frames */
+    U32 TX_maxcol;    /* frames not TX due to MAX collisions */
+    U32 TX_latecol;   /* frames not TX due to late collisions */
+    U32 TX_urun;      /* frames not TX due to DMA underrun */
+    U32 TX_crs;       /* frames TX with lost carrier sense */
+    U32 TX_def;       /* frames deferred due to activity on link */
+    U32 TX_singlecol; /* frames TX with one and only on collision */
+    U32 TX_multcol;   /* frames TX with more than one collision */
+    U32 TX_totcol;    /* total collisions detected during TX */
+    U32 Rcv_good;     /* good frames received */
+    U32 Rcv_CRCerr;   /* frames RX and discarded with CRC errors */
+    U32 Rcv_alignerr; /* frames RX with alignment and CRC errors */
+    U32 Rcv_reserr;   /* good frames discarded due to no RX buffer */
+    U32 Rcv_orun;     /* RX frames lost due to FIFO overrun */
+    U32 Rcv_cdt;      /* RX frames with collision during RX */
+    U32 Rcv_runt;     /* RX frames shorter than 64 bytes */
+}
+ RCLINKSTATS, *P_RCLINKSTATS;
+
+ /*
+ ** RCGetLinkStatistics()
+ **
+ ** Returns link statistics in user's structure at address StatsReturnAddr
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatistics(U16 AdapterID,
+                              P_RCLINKSTATS StatsReturnAddr,
+                              PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCGetLinkStatus()
+ **
+ ** Return link status, up or down, to user's location addressed by ReturnAddr.
+ ** If given, not NULL, the function WaitCallback is called during the wait
+ ** loop while waiting for the adapter to respond.
+ */
+RC_RETURN RCGetLinkStatus(U16 AdapterID, 
+                          PU32 pReturnStatus,
+                          PFNWAITCALLBACK WaitCallback);
+                               
+ /* Link Status defines - value returned in pReturnStatus */
+#define LAN_LINK_STATUS_DOWN     0
+#define LAN_LINK_STATUS_UP       1
+
+ /*
+ ** RCGetMAC()
+ **
+ ** Get the current MAC address assigned to user.  RedCreek Ravlin 45/PCI 
+ ** has two MAC addresses.  One which is private to the PCI Card, and 
+ ** another MAC which is given to the user as its link layer MAC address. The
+ ** adapter runs in promiscous mode because of the dual address requirement.
+ ** The MAC address is returned to the unsigned char array pointer to by mac.
+ */
+RC_RETURN RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback);
+
+ /*
+ ** RCSetMAC()
+ **
+ ** Set a new user port MAC address.  This address will be returned on
+ ** subsequent RCGetMAC() calls.
+ */
+RC_RETURN RCSetMAC(U16 AdapterID, PU8 mac);
+
+ /*
+ ** RCSetLinkSpeed()
+ **
+ ** set adapter's link speed based on given input code.
+ */
+RC_RETURN RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode);
+ /* Set link speed codes */
+#define LNK_SPD_AUTO_NEG_NWAY   0
+#define LNK_SPD_100MB_FULL      1
+#define LNK_SPD_100MB_HALF      2
+#define LNK_SPD_10MB_FULL       3
+#define LNK_SPD_10MB_HALF       4
+
+
+
+
+ /*
+ ** RCGetLinkSpeed()
+ **
+ ** Return link speed code.
+ */
+ /* Return link speed codes */
+#define LNK_SPD_UNKNOWN         0
+#define LNK_SPD_100MB_FULL      1
+#define LNK_SPD_100MB_HALF      2
+#define LNK_SPD_10MB_FULL       3
+#define LNK_SPD_10MB_HALF       4
+
+RC_RETURN
+RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback);
+
+/*
+** =========================================================================
+** RCReportDriverCapability(U16 AdapterID, U32 capability)
+**
+** Currently defined bits:
+** WARM_REBOOT_CAPABLE   0x01
+**
+** =========================================================================
+*/
+RC_RETURN
+RCReportDriverCapability(U16 AdapterID, U32 capability);
+
+/*
+** RCGetFirmwareVer()
+**
+** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
+**
+** WARNING: user's space pointed to by pFirmString should be at least 60 bytes.
+*/
+RC_RETURN
+RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback);
+
+/*
+** ----------------------------------------------
+** LAN adapter Reset and Shutdown functions
+** ----------------------------------------------
+*/
+ /* resource flag bit assignments for RCResetLANCard() & RCShutdownLANCard() */
+#define RC_RESOURCE_RETURN_POSTED_RX_BUCKETS  0x0001 
+#define RC_RESOURCE_RETURN_PEND_TX_BUFFERS    0x0002
+
+ /*
+ ** RCResetLANCard()
+ **
+ ** Reset LAN card operation.  Causes a software reset of the ethernet
+ ** controller and restarts the command and receive units. Depending on 
+ ** the ResourceFlags given, the buffers are either returned to the
+ ** host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER and
+ ** detailed status of RC_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete.  If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
+ ** to complete (please disable adapter interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** A call to RCPostRecvBuffers() is needed to return the ethernet to full
+ ** operation if the receive buffers were returned during LANReset.
+ ** Note: The IOP status is not affected by a LAN reset.
+ */
+RC_RETURN RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+
+ /*
+ ** RCShutdownLANCard()
+ **
+ ** Shutdown LAN card operation and put into an idle (suspended) state.
+ ** The LAN card is restarted with RCResetLANCard() function.
+ ** Depending on the ResourceFlags given, the buffers are either returned 
+ ** to the host with reply status of RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 
+ ** and detailed status of RC_DSC_CANCELED (new receive buffers must be
+ ** posted after issuing this) OR the buffers are kept and reused by
+ ** the ethernet controller. If CallbackFunction is not NULL, the function
+ ** will be called when the reset is complete.  If the CallbackFunction is
+ ** NULL,a 1 will be put into the ReturnAddr after waiting for the reset 
+ ** to complete (please disable adapter interrupts during this method).
+ ** Any outstanding transmit or receive buffers that are complete will be
+ ** returned via the normal reply messages before the requested resource
+ ** buffers are returned.
+ ** Note: The IOP status is not affected by a LAN shutdown.
+ */                                      
+RC_RETURN 
+RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction);
+
+ /*
+ ** RCResetAdapter();
+ **     Initializes ADAPTERState to ADAPTER_STATE_RESET.
+ **     Stops access to outbound message Q.
+ **     Discards any outstanding transmit or posted receive buffers.
+ **     Clears outbound message Q. 
+ */
+RC_RETURN 
+RCResetAdapter(U16 AdapterID);
+
+#endif /* RCMTL_H */
diff -u --recursive --new-file v2.0.35/linux/drivers/net/rcpci45.c linux/drivers/net/rcpci45.c
--- v2.0.35/linux/drivers/net/rcpci45.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/rcpci45.c	Sun Nov 15 10:33:03 1998
@@ -0,0 +1,1332 @@
+/* 
+**  RCpci45.c  
+**
+**
+**
+**  ---------------------------------------------------------------------
+**  ---     Copyright (c) 1998, RedCreek Communications Inc.          ---
+**  ---                   All rights reserved.                        ---
+**  ---------------------------------------------------------------------
+**
+** Written by Pete Popov and Brian Moyle.
+**
+** Known Problems
+** 
+** Billions and Billions...  
+**
+** ... apparently added by Brian.  Pete knows of no bugs.
+**
+**  TODO:
+**      -Get rid of the wait loops in the API and replace them
+**       with system independent delays ...something like
+**       "delayms(2)".
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either version 2 of the License, or
+**  (at your option) any later version.
+
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+***************************************************************************/
+
+#define __NO_VERSION__ /* don't define kernel_verion in module.h */
+
+static char *version =
+"RedCreek Communications PCI linux driver version 1.32 Beta\n";
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+static char kernel_version [] = UTS_RELEASE;
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/timer.h>
+#include <asm/irq.h>            /* For NR_IRQS only. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#define RC_LINUX_MODULE
+#include "rcmtl.h"
+#include "rcif.h"
+
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+
+#define NEW_MULTICAST
+#include <linux/delay.h>
+
+/* PCI/45 Configuration space values */
+#define RC_PCI45_VENDOR_ID  0x4916
+#define RC_PCI45_DEVICE_ID  0x1960
+
+#define MAX_ETHER_SIZE        1520  
+#define MAX_NMBR_RCV_BUFFERS    96
+#define RC_POSTED_BUFFERS_LOW_MARK MAX_NMBR_RCV_BUFFERS-16
+#define BD_SIZE 3           /* Bucket Descriptor size */
+#define BD_LEN_OFFSET 2     /* Bucket Descriptor offset to length field */
+
+
+/* RedCreek LAN device Target ID */
+#define RC_LAN_TARGET_ID  0x10 
+/* RedCreek's OSM default LAN receive Initiator */
+#define DEFAULT_RECV_INIT_CONTEXT  0xA17  
+
+
+static U32 DriverControlWord =  0;
+
+static void rc_timer(unsigned long);
+
+/*
+ * Driver Private Area, DPA.
+ */
+typedef struct
+{
+
+     /* 
+      *    pointer to the device structure which is part
+      * of the interface to the Linux kernel.
+      */
+      struct device *dev;            
+     
+     char devname[8];                /* "ethN" string */
+     U8     id;                        /* the AdapterID */
+     U32    pci_addr;               /* the pci address of the adapter */
+     U32    bus;
+     U32    function;
+     struct timer_list timer;        /*  timer */
+     struct enet_statistics  stats; /* the statistics structure */
+     struct device *next;            /* points to the next RC adapter */
+     unsigned long numOutRcvBuffers;/* number of outstanding receive buffers*/
+     unsigned char shutdown;
+     unsigned char reboot;
+     unsigned char nexus;
+     PU8    PLanApiPA;             /* Pointer to Lan Api Private Area */
+
+}
+DPA, *PDPA;
+
+#define MAX_ADAPTERS 32
+
+static PDPA  PCIAdapters[MAX_ADAPTERS] = 
+{
+     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+
+static int RCscan(void);
+static struct device 
+*RCfound_device(struct device *, int, int, int, int, int, int);
+
+static int RCprobe1(struct device *);
+static int RCopen(struct device *);
+static int RC_xmit_packet(struct sk_buff *, struct device *);
+static void RCinterrupt(int, void *, struct pt_regs *);
+static int RCclose(struct device *dev);
+static struct enet_statistics *RCget_stats(struct device *);
+static int RCioctl(struct device *, struct ifreq *, int);
+static int RCconfig(struct device *, struct ifmap *);
+static void RCxmit_callback(U32, U16, PU32, U16);
+static void RCrecv_callback(U32, U8, U32, PU32, U16);
+static void RCreset_callback(U32, U32, U32, U16);
+static void RCreboot_callback(U32, U32, U32, U16);
+static int RC_allocate_and_post_buffers(struct device *, int);
+
+
+/* A list of all installed RC devices, for removing the driver module. */
+static struct device *root_RCdev = NULL;
+
+#ifdef MODULE
+int init_module(void)
+#else
+int rcpci_probe(struct netdevice *dev)
+#endif
+{
+     int cards_found;
+
+     printk(version);
+
+     root_RCdev = NULL;
+     cards_found = RCscan();
+#ifdef MODULE     
+     return cards_found ? 0 : -ENODEV;
+#else
+     return -1;
+#endif          
+}
+
+static int RCscan(void)
+{
+     int cards_found = 0;
+     struct device *dev = 0;
+
+     if (pcibios_present()) 
+     {
+          static int pci_index = 0;
+          unsigned char pci_bus, pci_device_fn;
+          int scan_status;
+          int board_index = 0;
+
+          for (;pci_index < 0xff; pci_index++) 
+          {
+               unsigned char pci_irq_line;
+               unsigned short pci_command, vendor, device, class;
+               unsigned int pci_ioaddr;
+
+
+               scan_status =  
+                    (pcibios_find_device (RC_PCI45_VENDOR_ID, 
+                                          RC_PCI45_DEVICE_ID, 
+                                          pci_index, 
+                                          &pci_bus, 
+                                          &pci_device_fn));
+#ifdef RCDEBUG
+               printk("rc scan_status = 0x%X\n", scan_status);
+#endif
+               if (scan_status != PCIBIOS_SUCCESSFUL)
+                    break;
+               pcibios_read_config_word(pci_bus, 
+                                        pci_device_fn, 
+                                        PCI_VENDOR_ID, &vendor);
+               pcibios_read_config_word(pci_bus, 
+                                        pci_device_fn,
+                                        PCI_DEVICE_ID, &device);
+               pcibios_read_config_byte(pci_bus, 
+                                        pci_device_fn,
+                                        PCI_INTERRUPT_LINE, &pci_irq_line);
+               pcibios_read_config_dword(pci_bus, 
+                                         pci_device_fn,
+                                         PCI_BASE_ADDRESS_0, &pci_ioaddr);
+               pcibios_read_config_word(pci_bus, 
+                                        pci_device_fn,
+                                        PCI_CLASS_DEVICE, &class);
+
+               pci_ioaddr &= ~0xf;
+
+#ifdef RCDEBUG
+               printk("rc: Found RedCreek PCI adapter\n");
+               printk("rc: pci class = 0x%x  0x%x \n", class, class>>8);
+               printk("rc: pci_bus = %d,  pci_device_fn = %d\n", pci_bus, pci_device_fn);
+               printk("rc: pci_irq_line = 0x%x \n", pci_irq_line);
+               printk("rc: pci_ioaddr = 0x%x\n", pci_ioaddr);
+#endif
+
+#if 0
+               if (check_region(pci_ioaddr, 32768))
+               {
+                    printk("rc: check_region failed\n");
+                    continue;
+               }
+               else
+               {
+                    printk("rc: check_region passed\n");
+               }
+#endif
+               
+                /*
+                 * Get and check the bus-master and latency values.
+                 * Some PCI BIOSes fail to set the master-enable bit.
+                 */
+
+                pcibios_read_config_word(pci_bus, 
+                                         pci_device_fn,
+                                         PCI_COMMAND, 
+                                         &pci_command);
+                if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+                     printk("rc: PCI Master Bit has not been set!\n");
+                            
+                     pci_command |= PCI_COMMAND_MASTER;
+                     pcibios_write_config_word(pci_bus, 
+                                               pci_device_fn,
+                                               PCI_COMMAND, 
+                                               pci_command);
+                }
+                if ( ! (pci_command & PCI_COMMAND_MEMORY)) {
+                /*
+                 * If the BIOS did not set the memory enable bit, what else
+                 * did it not initialize?  Skip this adapter.
+                 */
+                     printk("rc: Adapter %d, PCI Memory Bit has not been set!\n",
+                         cards_found);
+                     printk("rc: Bios problem? \n");
+                     continue;
+                }
+                    
+               dev = RCfound_device(dev, pci_ioaddr, pci_irq_line,
+                                    pci_bus, pci_device_fn,
+                                    board_index++, cards_found);
+
+               if (dev) {
+                    dev = 0;
+                    cards_found++;
+               }
+          }
+     }
+     printk("rc: found %d cards \n", cards_found);
+     return cards_found;
+}
+
+static struct device *
+RCfound_device(struct device *dev, int memaddr, int irq, 
+               int bus, int function, int product_index, int card_idx)
+{
+     int dev_size = 32768;        
+     unsigned long *vaddr=0;
+     PDPA pDpa;
+     int init_status;
+
+     /* 
+      * Allocate and fill new device structure. 
+      * We need enough for struct device plus DPA plus the LAN API private
+      * area, which requires a minimum of 16KB.  The top of the allocated
+      * area will be assigned to struct device; the next chunk will be
+      * assigned to DPA; and finally, the rest will be assigned to the
+      * the LAN API layer.
+      */
+     dev = (struct device *) kmalloc(dev_size, GFP_DMA | GFP_KERNEL |GFP_ATOMIC);
+     memset(dev, 0, dev_size);
+#ifdef RCDEBUG
+     printk("rc: dev = 0x%08X\n", (uint)dev);
+#endif 
+
+     /*
+      * dev->priv will point to the start of DPA.
+      */
+     dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
+     pDpa = dev->priv;
+     dev->name = pDpa->devname;
+
+     pDpa->dev = dev;            /* this is just for easy reference */
+     pDpa->function = function;
+     pDpa->bus = bus;
+     pDpa->id = card_idx;        /* the device number */
+     pDpa->pci_addr = memaddr;
+     PCIAdapters[card_idx] = pDpa;
+#ifdef RCDEBUG
+     printk("rc: pDpa = 0x%x, id = %d \n", (uint)pDpa, (uint)pDpa->id);
+#endif
+
+     /*
+      * Save the starting address of the LAN API private area.  We'll
+      * pass that to InitRCApiMsgLayer().
+      */
+     pDpa->PLanApiPA = (void *)(((long)pDpa + sizeof(DPA) + 0xff) & ~0xff);
+#ifdef RCDEBUG
+     printk("rc: pDpa->PLanApiPA = 0x%x\n", (uint)pDpa->PLanApiPA);
+#endif
+    
+     /* The adapter is accessable through memory-access read/write, not
+      * I/O read/write.  Thus, we need to map it to some virtual address
+      * area in order to access the registers are normal memory.
+      */
+     vaddr = (ulong *) vremap (memaddr, 32768);
+#ifdef RCDEBUG
+     printk("rc: RCfound_device: 0x%x, priv = 0x%x, vaddr = 0x%x\n", 
+            (uint)dev, (uint)dev->priv, (uint)vaddr);
+#endif
+     dev->base_addr = (unsigned long)vaddr;
+     dev->irq = irq;
+     dev->interrupt = 0;
+
+     /*
+      * Request a shared interrupt line.
+      */
+     if ( request_irq(dev->irq, (void *)RCinterrupt,
+                      SA_INTERRUPT|SA_SHIRQ, "RedCreek VPN Adapter", dev) )
+     {
+          printk( "RC PCI 45: %s: unable to get IRQ %d\n", (PU8)dev->name, (uint)dev->irq );
+          vfree(vaddr);
+          kfree(dev);
+           return 0;
+     }
+
+     init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, 
+                                     pDpa->PLanApiPA, pDpa->PLanApiPA,
+                                     (PFNTXCALLBACK)RCxmit_callback,
+                                     (PFNRXCALLBACK)RCrecv_callback,
+                                     (PFNCALLBACK)RCreboot_callback);
+#ifdef RCDEBUG
+     printk("rc: msg initted: status = 0x%x\n", init_status);
+#endif
+     if (init_status)
+     {
+          printk("rc: Unable to initialize msg layer\n");
+          free_irq(dev->irq, dev);
+          vfree(vaddr);
+          kfree(dev);
+          return 0;
+     }
+     if (RCGetMAC(pDpa->id, dev->dev_addr, NULL))
+     {
+          printk("rc: Unable to get adapter MAC\n");
+          free_irq(dev->irq, dev);
+          vfree(vaddr);
+          kfree(dev);
+          return 0;
+     }
+
+     DriverControlWord |= WARM_REBOOT_CAPABLE;
+     RCReportDriverCapability(pDpa->id, DriverControlWord);
+
+     dev->init = RCprobe1;
+     ether_setup(dev);            /* linux kernel interface */
+
+     pDpa->next = root_RCdev;
+     root_RCdev = dev;
+
+     if (register_netdev(dev) != 0) /* linux kernel interface */
+     {
+          printk("rc: unable to register device \n");
+          free_irq(dev->irq, dev);
+          vfree(vaddr);
+          kfree(dev);
+          return 0;
+     }
+     return dev;
+}
+
+static int RCprobe1(struct device *dev)
+{
+     dev->open = RCopen;
+     dev->hard_start_xmit = RC_xmit_packet;
+     dev->stop = RCclose;
+     dev->get_stats = RCget_stats;
+     dev->do_ioctl = RCioctl;
+     dev->set_config = RCconfig;
+     return 0;
+}
+
+static int
+RCopen(struct device *dev)
+{
+    int post_buffers = MAX_NMBR_RCV_BUFFERS;
+    PDPA pDpa = (PDPA) dev->priv;
+    int count = 0;
+    int requested = 0;
+
+#ifdef RCDEBUG
+    printk("rc: RCopen\n");
+#endif
+    RCEnableAdapterInterrupts(pDpa->id);
+
+    if (pDpa->nexus)
+    {
+       /* This is not the first time RCopen is called.  Thus,
+        * the interface was previously opened and later closed
+        * by RCclose().  RCclose() does a Shutdown; to wake up
+        * the adapter, a reset is mandatory before we can post
+        * receive buffers.  However, if the adapter initiated 
+        * a reboot while the interface was closed -- and interrupts
+        * were turned off -- we need will need to reinitialize
+        * the adapter, rather than simply waking it up.  
+        */
+        printk("rc: Waking up adapter...\n");
+        RCResetLANCard(pDpa->id,0,0,0);
+    }
+    else
+    {
+       pDpa->nexus = 1;
+    }
+
+    while(post_buffers)
+    {
+        if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+            requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
+        else
+            requested = post_buffers;
+        count = RC_allocate_and_post_buffers(dev, requested);
+
+        if ( count < requested )
+        {
+             /*
+              * Check to see if we were able to post any buffers at all.
+              */
+             if (post_buffers == MAX_NMBR_RCV_BUFFERS)
+             {
+                  printk("rc: Error RCopen: not able to allocate any buffers\r\n");
+                  return(-ENOMEM);                    
+             }
+             printk("rc: Warning RCopen: not able to allocate all requested buffers\r\n");
+             break;            /* we'll try to post more buffers later */
+        }
+        else
+             post_buffers -= count;
+    }
+    pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers;
+    pDpa->shutdown = 0;        /* just in case */
+#ifdef RCDEBUG
+    printk("rc: RCopen: posted %d buffers\n", (uint)pDpa->numOutRcvBuffers);
+#endif
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int
+RC_xmit_packet(struct sk_buff *skb, struct device *dev)
+{
+
+     PDPA pDpa = (PDPA) dev->priv;
+     singleTCB tcb;
+     psingleTCB ptcb = &tcb;
+     RC_RETURN status = 0;
+    
+     if (dev->tbusy || pDpa->shutdown || pDpa->reboot)
+     {
+#ifdef RCDEBUG
+          printk("rc: RC_xmit_packet: tbusy!\n");
+#endif
+          return 1;
+     }
+      
+     if ( skb->len <= 0 ) 
+     {
+          printk("RC_xmit_packet: skb->len less than 0!\n");
+          return 0;
+     }
+
+     /*
+      * The user is free to reuse the TCB after RCSendPacket() returns, since
+      * the function copies the necessary info into its own private space.  Thus,
+      * our TCB can be a local structure.  The skb, on the other hand, will be
+      * freed up in our interrupt handler.
+      */
+     ptcb->bcount = 1;
+     /* 
+      * we'll get the context when the adapter interrupts us to tell us that
+      * the transmision is done. At that time, we can free skb.
+      */
+     ptcb->b.context = (U32)skb;    
+     ptcb->b.scount = 1;
+     ptcb->b.size = skb->len;
+     ptcb->b.addr = (U32)skb->data;
+
+#ifdef RCDEBUG
+     printk("rc: RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n", 
+            (uint)skb, (uint)pDpa, (uint)pDpa->id, (uint)ptcb);
+#endif
+     if ( (status = RCSendPacket(pDpa->id, (U32)NULL, (PRCTCB)ptcb))
+          != RC_RTN_NO_ERROR)
+     {
+#ifdef RCDEBUG
+          printk("rc: RC send error 0x%x\n", (uint)status);
+#endif
+          dev->tbusy = 1;
+     }
+     else
+     {
+          dev->trans_start = jiffies;
+          //       dev->tbusy = 0;
+     }
+     /*
+      * That's it!
+      */
+     return 0;
+}
+
+/*
+ * RCxmit_callback()
+ *
+ * The transmit callback routine. It's called by RCProcMsgQ()
+ * because the adapter is done with one or more transmit buffers and
+ * it's returning them to us, or we asked the adapter to return the
+ * outstanding transmit buffers by calling RCResetLANCard() with 
+ * RC_RESOURCE_RETURN_PEND_TX_BUFFERS flag. 
+ * All we need to do is free the buffers.
+ */
+static void 
+RCxmit_callback(U32 Status, 
+                U16 PcktCount, 
+                PU32 BufferContext, 
+                U16 AdapterID)
+{
+    struct sk_buff *skb;
+    PDPA pDpa;
+    struct device *dev;
+
+    pDpa = PCIAdapters[AdapterID];
+    if (!pDpa)
+    {
+        printk("rc: Fatal error: xmit callback, !pDpa\n");
+        return;
+    }
+    dev = pDpa->dev;
+
+        // printk("xmit_callback: Status = 0x%x\n", (uint)Status);
+    if (Status != RC_REPLY_STATUS_SUCCESS)
+    {
+        printk("rc: xmit_callback: Status = 0x%x\n", (uint)Status);
+    }
+#ifdef RCDEBUG
+    if (pDpa->shutdown || pDpa->reboot)
+        printk("rc: xmit callback: shutdown||reboot\n");
+#endif
+
+#ifdef RCDEBUG     
+    printk("rc: xmit_callback: PcktCount = %d, BC = 0x%x\n", 
+            (uint)PcktCount, (uint)BufferContext);
+#endif
+    while (PcktCount--)
+    {
+        skb = (struct sk_buff *)(BufferContext[0]);
+#ifdef RCDEBUG
+        printk("rc: skb = 0x%x\n", (uint)skb);
+#endif
+        BufferContext++;
+        dev_kfree_skb (skb, FREE_WRITE);
+    }
+    dev->tbusy = 0;
+
+}
+
+static void
+RCreset_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
+{
+     PDPA pDpa;
+     struct device *dev;
+     
+     pDpa = PCIAdapters[AdapterID];
+     dev = pDpa->dev;
+#ifdef RCDEBUG
+      printk("rc: RCreset_callback Status 0x%x\n", (uint)Status);
+#endif
+      /*
+       * Check to see why we were called.
+       */
+      if (pDpa->shutdown)
+      {
+           printk("rc: Shutting down interface\n");
+           pDpa->shutdown = 0;
+           pDpa->reboot = 0;
+           MOD_DEC_USE_COUNT; 
+      }
+      else if (pDpa->reboot)
+      {
+           printk("rc: reboot, shutdown adapter\n");
+           /*
+            * We don't set any of the flags in RCShutdownLANCard()
+            * and we don't pass a callback routine to it.
+            * The adapter will have already initiated the reboot by
+            * the time the function returns.
+            */
+		   RCDisableAdapterInterrupts(pDpa->id);
+           RCShutdownLANCard(pDpa->id,0,0,0);
+           printk("rc: scheduling timer...\n");
+           init_timer(&pDpa->timer);
+           pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+           pDpa->timer.data = (unsigned long)dev;
+           pDpa->timer.function = &rc_timer;    /* timer handler */
+           add_timer(&pDpa->timer);
+      }
+
+
+
+}
+
+static void
+RCreboot_callback(U32 Status, U32 p1, U32 p2, U16 AdapterID)
+{
+     PDPA pDpa;
+    
+     pDpa = PCIAdapters[AdapterID];
+#ifdef RCDEBUG
+     printk("rc: RCreboot: rcv buffers outstanding = %d\n", 
+            (uint)pDpa->numOutRcvBuffers);
+#endif
+     if (pDpa->shutdown)
+     {
+          printk("rc: skipping reboot sequence -- shutdown already initiated\n");
+          return;
+     }
+     pDpa->reboot = 1;
+     /*
+      * OK, we reset the adapter and ask it to return all
+      * outstanding transmit buffers as well as the posted
+      * receive buffers.  When the adapter is done returning
+      * those buffers, it will call our RCreset_callback() 
+      * routine.  In that routine, we'll call RCShutdownLANCard()
+      * to tell the adapter that it's OK to start the reboot and
+      * schedule a timer callback routine to execute 3 seconds 
+      * later; this routine will reinitialize the adapter at that time.
+      */
+     RCResetLANCard(pDpa->id, 
+                    RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
+                    RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+                    (PFNCALLBACK)RCreset_callback);
+}
+
+
+int broadcast_packet(unsigned char * address)
+{
+    int i;
+    for (i=0; i<6; i++)
+        if (address[i] != 0xff) return 0;
+
+    return 1;
+}
+
+/*
+ * RCrecv_callback()
+ * 
+ * The receive packet callback routine.  This is called by
+ * RCProcMsgQ() after the adapter posts buffers which have been
+ * filled (one ethernet packet per buffer).
+ */
+static void
+RCrecv_callback(U32  Status, 
+                U8   PktCount, 
+                U32  BucketsRemain, 
+                PU32 PacketDescBlock, 
+                U16  AdapterID)
+{
+
+     U32 len, count;
+     PDPA pDpa;
+     struct sk_buff *skb;
+     struct device *dev;
+     singleTCB tcb;
+     psingleTCB ptcb = &tcb;
+
+
+          pDpa = PCIAdapters[AdapterID];
+          dev = pDpa->dev;
+
+          ptcb->bcount = 1;
+
+#ifdef RCDEBUG
+          printk("rc: RCrecv_callback: 0x%x, 0x%x, 0x%x\n",
+                 (uint)PktCount, (uint)BucketsRemain, (uint)PacketDescBlock);
+#endif
+        
+#ifdef RCDEBUG
+          if ((pDpa->shutdown || pDpa->reboot) && !Status)
+               printk("shutdown||reboot && !Status: PktCount = %d\n",PktCount);
+#endif
+
+          if ( (Status != RC_REPLY_STATUS_SUCCESS) || pDpa->shutdown)
+          {
+               /*
+                * Free whatever buffers the adapter returned, but don't
+                * pass them to the kernel.
+                */
+        
+                    if (!pDpa->shutdown && !pDpa->reboot)
+                         printk("rc: RCrecv error: status = 0x%x\n", (uint)Status);
+                    else
+                         printk("rc: Returning %d buffers, status = 0x%x\n", 
+                                PktCount, (uint)Status);
+               /*
+                * TO DO: check the nature of the failure and put the adapter in
+                * failed mode if it's a hard failure.  Send a reset to the adapter
+                * and free all outstanding memory.
+                */
+               if (Status == RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER)
+               {
+#ifdef RCDEBUG
+                    printk("RCrecv status ABORT NO DATA TRANSFER\n");
+#endif
+               }
+               /* check for reset status: RC_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */ 
+               if (PacketDescBlock)
+               {
+                    while(PktCount--)
+                    {
+                         skb = (struct sk_buff *)PacketDescBlock[0];
+                         skb->free = 1;
+                         skb->lock = 0;    
+#ifdef RCDEBUG
+                         printk("free skb 0x%p\n", skb);
+#endif
+                         dev_kfree_skb(skb, FREE_READ);
+                         pDpa->numOutRcvBuffers--;
+                         PacketDescBlock += BD_SIZE; /* point to next context field */
+                    }
+               }
+               return;
+          }
+          else
+          {
+               while(PktCount--)
+               {
+                    skb = (struct sk_buff *)PacketDescBlock[0];
+#ifdef RCDEBUG
+                    if (pDpa->shutdown)
+                         printk("shutdown: skb=0x%x\n", (uint)skb);
+
+                    printk("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", (uint)skb,
+                           (uint)skb->data[0], (uint)skb->data[1], (uint)skb->data[2],
+                           (uint)skb->data[3], (uint)skb->data[4], (uint)skb->data[5]);
+#endif
+                    if ( (memcmp(dev->dev_addr, skb->data, 6)) &&
+                         (!broadcast_packet(skb->data)))
+                    {
+                         /*
+                          * Re-post the buffer to the adapter.  Since the adapter usually
+                          * return 1 to 2 receive buffers at a time, it's not too inefficient
+                          * post one buffer at a time but ... may be that should be 
+                          * optimized at some point.
+                          */
+                         ptcb->b.context = (U32)skb;    
+                         ptcb->b.scount = 1;
+                         ptcb->b.size = MAX_ETHER_SIZE;
+                         ptcb->b.addr = (U32)skb->data;
+
+                         if ( RCPostRecvBuffers(pDpa->id, (PRCTCB)ptcb ) != RC_RTN_NO_ERROR)
+                         {
+                              printk("rc: RCrecv_callback: post buffer failed!\n");
+                              skb->free = 1;
+                              dev_kfree_skb(skb, FREE_READ);
+                         }
+                         else
+                         {
+                              pDpa->numOutRcvBuffers++;
+                         }
+                    }
+                    else
+                    {
+                         len = PacketDescBlock[2];
+                         skb->dev = dev;
+                         skb_put( skb, len ); /* adjust length and tail */
+                         skb->protocol = eth_type_trans(skb, dev);
+                         netif_rx(skb);    /* send the packet to the kernel */
+                         dev->last_rx = jiffies;
+                    }
+                    pDpa->numOutRcvBuffers--;
+                    PacketDescBlock += BD_SIZE; /* point to next context field */
+               }
+          } 
+    
+          /*
+           * Replenish the posted receive buffers. 
+           * DO NOT replenish buffers if the driver has already
+           * initiated a reboot or shutdown!
+           */
+
+          if (!pDpa->shutdown && !pDpa->reboot)
+          {
+               count = RC_allocate_and_post_buffers(dev, 
+                                                    MAX_NMBR_RCV_BUFFERS-pDpa->numOutRcvBuffers);
+               pDpa->numOutRcvBuffers += count;
+          }
+
+}
+
+/*
+ * RCinterrupt()
+ * 
+ * Interrupt handler. 
+ * This routine sets up a couple of pointers and calls
+ * RCProcMsgQ(), which in turn process the message and
+ * calls one of our callback functions.
+ */
+static void 
+RCinterrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+
+     PDPA pDpa;
+     struct device *dev = (struct device *)(dev_id);
+
+     pDpa = (PDPA) (dev->priv);
+     
+     if (pDpa->shutdown)
+          printk("rc: shutdown: service irq\n");
+
+#ifdef RCDEBUG
+     printk("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n", 
+            (uint)pDpa, (uint)dev, (uint)pDpa->id);
+     printk("dev = 0x%x\n", (uint)dev);
+#endif
+     if (dev->interrupt)
+          printk("%s: Re-entering the interrupt handler.\n", dev->name);
+     dev->interrupt = 1;
+
+     RCProcMsgQ(pDpa->id);
+     dev->interrupt = 0;
+
+     return;
+}
+
+#define REBOOT_REINIT_RETRY_LIMIT 10
+static void rc_timer(unsigned long data)
+{
+     struct device *dev = (struct device *)data;
+     PDPA pDpa = (PDPA) (dev->priv);
+     int init_status;
+     static int retry = 0;
+     int post_buffers = MAX_NMBR_RCV_BUFFERS;
+     int count = 0;
+     int requested = 0;
+
+     if (pDpa->reboot)
+     {
+
+          init_status = InitRCApiMsgLayer(pDpa->id, dev->base_addr, 
+                                          pDpa->PLanApiPA, pDpa->PLanApiPA,
+                                          (PFNTXCALLBACK)RCxmit_callback,
+                                          (PFNRXCALLBACK)RCrecv_callback,
+                                          (PFNCALLBACK)RCreboot_callback);
+
+          switch(init_status)
+          {
+          case RC_RTN_NO_ERROR:
+ 
+               pDpa->reboot = 0;
+               pDpa->shutdown = 0;        /* just in case */
+               RCReportDriverCapability(pDpa->id, DriverControlWord);
+               RCEnableAdapterInterrupts(pDpa->id);
+
+               if (dev->flags & IFF_UP)
+               {
+                    while(post_buffers)
+                    {
+                         if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+                              requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
+                         else
+                              requested = post_buffers;
+                         count = RC_allocate_and_post_buffers(dev, requested);
+                         post_buffers -= count;
+                         if ( count < requested )
+                              break;
+                    }
+                    pDpa->numOutRcvBuffers = 
+                         MAX_NMBR_RCV_BUFFERS - post_buffers;
+                    printk("rc: posted %d buffers \r\n", 
+                           (uint)pDpa->numOutRcvBuffers);
+               }
+               printk("rc: Initialization done.\n");
+               return;
+          case RC_RTN_FREE_Q_EMPTY:
+               retry++;
+               printk("rc: inbound free q emtpy\n");
+               break;
+          default:
+               retry++;
+               printk("rc: unexpected bad status after reboot\n");    
+               break;
+          }
+
+          if (retry > REBOOT_REINIT_RETRY_LIMIT)
+          {
+               printk("rc: unable to reinitialize adapter after reboot\n");
+               printk("rc: decrementing driver and closing interface\n");
+               RCDisableAdapterInterrupts(pDpa->id);
+               dev->flags &= ~IFF_UP;
+               MOD_DEC_USE_COUNT; 
+          }
+          else
+          {
+               printk("rc: rescheduling timer...\n");
+               init_timer(&pDpa->timer);
+               pDpa->timer.expires = RUN_AT((30*HZ)/10); /* 3 sec. */
+               pDpa->timer.data = (unsigned long)dev;
+               pDpa->timer.function = &rc_timer;    /* timer handler */
+               add_timer(&pDpa->timer);
+          }
+     }
+     else
+     {
+          printk("rc: timer??\n");
+     }
+}
+
+static int
+RCclose(struct device *dev)
+{
+
+     PDPA pDpa = (PDPA) dev->priv;
+
+#ifdef RCDEBUG
+     printk("rc: RCclose\r\n");
+#endif
+     if (pDpa->reboot)
+     {
+          printk("rc: skipping reset -- adapter already in reboot mode\n");
+          dev->flags &= ~IFF_UP;
+          pDpa->shutdown = 1;
+          return 0;
+     }
+#ifdef RCDEBUG
+     printk("rc: receive buffers outstanding: %d\n", 
+            (uint)pDpa->numOutRcvBuffers);
+#endif
+
+     pDpa->shutdown = 1;
+
+     /*
+      * We can't allow the driver to be unloaded until the adapter returns
+      * all posted receive buffers.  It doesn't hurt to tell the adapter
+      * to return all posted receive buffers and outstanding xmit buffers,
+      * even if there are none.
+      */
+
+     RCShutdownLANCard(pDpa->id, 
+                       RC_RESOURCE_RETURN_POSTED_RX_BUCKETS | 
+                       RC_RESOURCE_RETURN_PEND_TX_BUFFERS,0,
+                       (PFNCALLBACK)RCreset_callback);
+
+     dev->flags &= ~IFF_UP;
+     return 0;
+}
+
+static struct enet_statistics *
+RCget_stats(struct device *dev)
+{
+    RCLINKSTATS    RCstats;
+   
+    PDPA pDpa = dev->priv;
+
+    if (!pDpa)
+    {
+        printk("rc: RCget_stats: !pDpa\n");
+        return 0;
+    }
+    else if (!(dev->flags & IFF_UP))    
+    {
+#ifdef RCDEBUG
+        printk("rc: RCget_stats: device down\n");
+#endif
+        return 0;
+    }
+
+    memset(&RCstats, 0, sizeof(RCLINKSTATS));
+    if ( (RCGetLinkStatistics(pDpa->id, &RCstats, (void *)0)) == RC_RTN_NO_ERROR )
+    {
+#ifdef RCDEBUG
+        printk("rc: TX_good 0x%x\n", (uint)RCstats.TX_good);
+        printk("rc: TX_maxcol 0x%x\n", (uint)RCstats.TX_maxcol);
+        printk("rc: TX_latecol 0x%x\n", (uint)RCstats.TX_latecol);
+        printk("rc: TX_urun 0x%x\n", (uint)RCstats.TX_urun);
+        printk("rc: TX_crs 0x%x\n", (uint)RCstats.TX_crs);
+        printk("rc: TX_def 0x%x\n", (uint)RCstats.TX_def);
+        printk("rc: TX_singlecol 0x%x\n", (uint)RCstats.TX_singlecol);
+        printk("rc: TX_multcol 0x%x\n", (uint)RCstats.TX_multcol);
+        printk("rc: TX_totcol 0x%x\n", (uint)RCstats.TX_totcol);
+
+        printk("rc: Rcv_good 0x%x\n", (uint)RCstats.Rcv_good);
+        printk("rc: Rcv_CRCerr 0x%x\n", (uint)RCstats.Rcv_CRCerr);
+        printk("rc: Rcv_alignerr 0x%x\n", (uint)RCstats.Rcv_alignerr);
+        printk("rc: Rcv_reserr 0x%x\n", (uint)RCstats.Rcv_reserr);
+        printk("rc: Rcv_orun 0x%x\n", (uint)RCstats.Rcv_orun);
+        printk("rc: Rcv_cdt 0x%x\n", (uint)RCstats.Rcv_cdt);
+        printk("rc: Rcv_runt 0x%x\n", (uint)RCstats.Rcv_runt);
+#endif
+
+        pDpa->stats.rx_packets = RCstats.Rcv_good; /* total packets received    */
+        pDpa->stats.tx_packets = RCstats.TX_good; /* total packets transmitted    */
+
+        pDpa->stats.rx_errors = 
+            RCstats.Rcv_CRCerr +
+            RCstats.Rcv_alignerr + 
+            RCstats.Rcv_reserr + 
+            RCstats.Rcv_orun + 
+            RCstats.Rcv_cdt + 
+            RCstats.Rcv_runt; /* bad packets received        */
+
+        pDpa->stats.tx_errors = 
+            RCstats.TX_urun + 
+            RCstats.TX_crs + 
+            RCstats.TX_def + 
+            RCstats.TX_totcol; /* packet transmit problems    */
+
+        /*
+         * This needs improvement.
+         */
+        pDpa->stats.rx_dropped = 0;        /* no space in linux buffers    */
+        pDpa->stats.tx_dropped = 0;        /* no space available in linux    */
+        pDpa->stats.multicast = 0;        /* multicast packets received    */
+        pDpa->stats.collisions = RCstats.TX_totcol;
+
+        /* detailed rx_errors: */
+        pDpa->stats.rx_length_errors = 0;
+        pDpa->stats.rx_over_errors = RCstats.Rcv_orun; /* receiver ring buff overflow    */
+        pDpa->stats.rx_crc_errors = RCstats.Rcv_CRCerr;    /* recved pkt with crc error    */
+        pDpa->stats.rx_frame_errors = 0; /* recv'd frame alignment error */
+        pDpa->stats.rx_fifo_errors = 0; /* recv'r fifo overrun        */
+        pDpa->stats.rx_missed_errors = 0; /* receiver missed packet    */
+
+        /* detailed tx_errors */
+        pDpa->stats.tx_aborted_errors = 0;
+        pDpa->stats.tx_carrier_errors = 0;
+        pDpa->stats.tx_fifo_errors = 0;
+        pDpa->stats.tx_heartbeat_errors = 0;
+        pDpa->stats.tx_window_errors = 0;
+
+        return ((struct enet_statistics *)&(pDpa->stats));
+    }
+    return 0;
+}
+
+static int RCioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+    RCuser_struct RCuser;
+    PDPA pDpa = dev->priv;
+
+#if RCDEBUG
+    printk("RCioctl: cmd = 0x%x\n", cmd);
+#endif
+ 
+    switch (cmd)  {
+ 
+        case RCU_PROTOCOL_REV:
+             /*
+              * Assign user protocol revision, to tell user-level
+              * controller program whether or not it's in sync.
+              */
+             rq->ifr_ifru.ifru_data = (caddr_t) USER_PROTOCOL_REV;
+             break;
+  
+
+        case RCU_COMMAND:
+        {
+            int error;
+
+            error=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(RCuser));
+            if (error)  {
+                return error;
+            }
+            memcpy_fromfs(&RCuser, rq->ifr_data, sizeof(RCuser));
+        
+#ifdef RCDEBUG
+            printk("RCioctl: RCuser_cmd = 0x%x\n", RCuser.cmd);
+#endif
+  
+            switch(RCuser.cmd)
+            {
+                case RCUC_GETFWVER:
+                    printk("RC GETFWVER\n");
+                    RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
+                    RCGetFirmwareVer(pDpa->id, (PU8) &RCUD_GETFWVER->FirmString, NULL);
+                    break;
+                case RCUC_GETINFO:
+                    printk("RC GETINFO\n");
+                    RCUD_GETINFO = &RCuser.RCUS_GETINFO;
+                    RCUD_GETINFO -> mem_start = dev->base_addr;
+                    RCUD_GETINFO -> mem_end = dev->base_addr + 32768;
+                    RCUD_GETINFO -> base_addr = pDpa->pci_addr;
+                    RCUD_GETINFO -> irq = dev->irq;
+                    break;
+                case RCUC_GETIPANDMASK:
+                    printk("RC GETIPANDMASK\n");
+                    RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
+                    RCGetRavlinIPandMask(pDpa->id, (PU32) &RCUD_GETIPANDMASK->IpAddr,
+                        (PU32) &RCUD_GETIPANDMASK->NetMask, NULL);
+                    break;
+                case RCUC_GETLINKSTATISTICS:
+                    printk("RC GETLINKSTATISTICS\n");
+                    RCUD_GETLINKSTATISTICS = &RCuser.RCUS_GETLINKSTATISTICS;
+                    RCGetLinkStatistics(pDpa->id, (P_RCLINKSTATS) &RCUD_GETLINKSTATISTICS->StatsReturn, NULL);
+                    break;
+                case RCUC_GETLINKSTATUS:
+                    printk("RC GETLINKSTATUS\n");
+                    RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
+                    RCGetLinkStatus(pDpa->id, (PU32) &RCUD_GETLINKSTATUS->ReturnStatus, NULL);
+                    break;
+                case RCUC_GETMAC:
+                    printk("RC GETMAC\n");
+                    RCUD_GETMAC = &RCuser.RCUS_GETMAC;
+                    RCGetMAC(pDpa->id, (PU8) &RCUD_GETMAC->mac, NULL);
+                    break;
+                case RCUC_GETSPEED:
+                    printk("RC GETSPEED\n");
+                    if (!(dev->flags & IFF_UP))    
+                    {
+                        printk("RCioctl, GETSPEED error: interface down\n");
+                        return -ENODATA;
+                    }
+                    RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
+                    RCGetLinkSpeed(pDpa->id, (PU32) &RCUD_GETSPEED->LinkSpeedCode, NULL);
+                    printk("RC speed = 0x%ld\n", RCUD_GETSPEED->LinkSpeedCode);
+                    break;
+                default:
+                    printk("RC command default\n");
+                    RCUD_DEFAULT = &RCuser.RCUS_DEFAULT;
+                    RCUD_DEFAULT -> rc = 0x11223344;
+                    break;
+            }
+            memcpy_tofs(rq->ifr_data, &RCuser, sizeof(RCuser));
+            break;
+        }   /* RCU_COMMAND */ 
+
+        default:
+            printk("RC default\n");
+            rq->ifr_ifru.ifru_data = (caddr_t) 0x12345678;
+            break;
+    }
+    return 0;
+}
+
+static int RCconfig(struct device *dev, struct ifmap *map)
+{
+     /*
+      * To be completed ...
+      */
+      printk("rc: RCconfig\n");
+      return 0;
+      if (dev->flags & IFF_UP)    /* can't act on a running interface */
+           return -EBUSY;
+
+     /* Don't allow changing the I/O address */
+     if (map->base_addr != dev->base_addr) {
+          printk(KERN_WARNING "RC pci45: Change I/O address not implemented\n");
+          return -EOPNOTSUPP;
+     }
+     return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+     PDPA pDpa;
+     struct device *next;
+
+
+#ifdef RCDEBUG
+     printk("rc: RC cleanup_module\n");
+     printk("rc: root_RCdev = 0x%x\n", (uint)root_RCdev);
+#endif
+
+
+     while (root_RCdev)
+     {
+          pDpa = (PDPA) root_RCdev->priv;
+#ifdef RCDEBUG
+          printk("rc: cleanup 0x%08X\n", (uint)root_RCdev);
+#endif
+          printk("Adapter reset: 0x%x\n", RCResetAdapter(pDpa->id));
+          unregister_netdev(root_RCdev);
+          next = pDpa->next;
+
+          vfree((unsigned long *)root_RCdev->base_addr); 
+          free_irq( root_RCdev->irq, root_RCdev );
+          kfree(root_RCdev);
+          root_RCdev = next;
+     }
+}
+#endif
+
+
+static int
+RC_allocate_and_post_buffers(struct device *dev, int numBuffers)
+{
+
+     int i;
+     PDPA pDpa = (PDPA)dev->priv;
+     PU32 p;
+     psingleB pB;
+     struct sk_buff *skb;
+     RC_RETURN status;
+
+     if (!numBuffers)
+          return 0;
+     else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
+     {
+#ifdef RCDEBUG
+          printk("rc: Too many buffers requested!\n");
+          printk("rc: attempting to allocate only 32 buffers\n");
+#endif
+          numBuffers = 32;
+     }
+    
+     p = (PU32) kmalloc(sizeof(U32) + numBuffers*sizeof(singleB), GFP_ATOMIC);
+
+#ifdef RCDEBUG
+     printk("rc: TCB = 0x%x\n", (uint)p);
+#endif
+
+     if (!p)
+     {
+          printk("rc: RCopen: unable to allocate TCB\n");
+          return 0;
+     }
+
+     p[0] = 0;                              /* Buffer Count */
+     pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+
+#ifdef RCDEBUG
+     printk("rc: p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint)p[0], (uint)p, (uint)pB);
+     printk("rc: pB = 0x%x\n", (uint)pB);
+#endif
+
+     for (i=0; i<numBuffers; i++)
+     {
+          skb = dev_alloc_skb(MAX_ETHER_SIZE+2);
+          if (!skb)
+          {
+               printk("rc: Doh! RCopen: unable to allocate enough skbs!\n");
+               if (*p != 0)        /* did we allocate any buffers at all? */
+               {
+#ifdef RCDEBUG
+                    printk("rc: will post only %d buffers \n", (uint)(*p));
+#endif
+                    break;
+               }
+               else 
+               {
+                    kfree(p);    /* Free the TCB */
+                    return 0;
+               }
+          }
+#ifdef RCDEBUG
+          printk("post 0x%x\n", (uint)skb);
+#endif
+          skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
+          pB->context = (U32)skb;
+          pB->scount = 1;        /* segment count */
+          pB->size = MAX_ETHER_SIZE;
+          pB->addr = (U32)skb->data;
+          p[0]++;
+          pB++;
+     }
+
+     if ( (status = RCPostRecvBuffers(pDpa->id, (PRCTCB)p )) != RC_RTN_NO_ERROR)
+     {
+          printk("rc: Post buffer failed with error code 0x%x!\n", status);
+          pB = (psingleB)((U32)p + sizeof(U32)); /* point to the first buffer */
+          while(p[0])
+          {
+               skb = (struct sk_buff *)pB->context;
+               skb->free = 1;    
+#ifdef RCDEBUG
+               printk("rc: freeing 0x%x\n", (uint)skb);
+#endif
+               dev_kfree_skb(skb, FREE_READ);
+               p[0]--;
+               pB++;
+          }
+#ifdef RCDEBUG
+           printk("rc: freed all buffers, p[0] = %ld\n", p[0]);
+#endif
+     }
+     kfree(p);
+     return(p[0]);                /* return the number of posted buffers */
+}
diff -u --recursive --new-file v2.0.35/linux/drivers/net/rtl8139.c linux/drivers/net/rtl8139.c
--- v2.0.35/linux/drivers/net/rtl8139.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/rtl8139.c	Sun Nov 15 10:33:03 1998
@@ -45,7 +45,6 @@
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  ((4000*HZ)/1000)
 
-#include <linux/config.h>
 #ifdef MODULE
 #ifdef MODVERSIONS
 #include <linux/modversions.h>
diff -u --recursive --new-file v2.0.35/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.0.35/linux/drivers/net/shaper.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/shaper.c	Sun Nov 15 10:33:03 1998
@@ -0,0 +1,666 @@
+/*
+ *			Simple traffic shaper for Linux NET3.
+ *
+ *	(c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved.
+ *				http://www.cymru.net
+ *
+ *	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.
+ *	
+ *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
+ *
+ *	
+ *	Algorithm:
+ *
+ *	Queue Frame:
+ *		Compute time length of frame at regulated speed
+ *		Add frame to queue at appropriate point
+ *		Adjust time length computation for followup frames
+ *		Any frame that falls outside of its boundaries is freed
+ *
+ *	We work to the following constants
+ *
+ *		SHAPER_QLEN	Maximum queued frames
+ *		SHAPER_LATENCY	Bounding latency on a frame. Leaving this latency
+ *				window drops the frame. This stops us queueing 
+ *				frames for a long time and confusing a remote
+ *				host.
+ *		SHAPER_MAXSLIP	Maximum time a priority frame may jump forward.
+ *				That bounds the penalty we will inflict on low
+ *				priority traffic.
+ *		SHAPER_BURST	Time range we call "now" in order to reduce
+ *				system load. The more we make this the burstier
+ *				the behaviour, the better local performance you
+ *				get through packet clustering on routers and the
+ *				worse the remote end gets to judge rtts.
+ *
+ *	This is designed to handle lower speed links ( < 200K/second or so). We
+ *	run off a 100-150Hz base clock typically. This gives us a resolution at
+ *	200Kbit/second of about 2Kbit or 256 bytes. Above that our timer
+ *	resolution may start to cause much more burstiness in the traffic. We
+ *	could avoid a lot of that by calling kick_shaper() at the end of the 
+ *	tied device transmissions. If you run above about 100K second you 
+ *	may need to tune the supposed speed rate for the right values.
+ *
+ *	BUGS:
+ *		Downing the interface under the shaper before the shaper
+ *		will render your machine defunct. Don't for now shape over
+ *		PPP or SLIP therefore!
+ *		This will be fixed in BETA4
+ */
+ 
+ 
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/if_shaper.h>
+
+int sh_debug;		/* Debug flag */
+
+#define SHAPER_BANNER	"Traffic Shaper 0.05 for Linux 2.0 <alan@redhat.com>\n"
+
+/*
+ *	Locking
+ */
+ 
+static int shaper_lock(struct shaper *sh)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	/*
+	 *	Lock in an interrupt may fail
+	 */
+	if(sh->locked && intr_count)
+	{
+		restore_flags(flags);
+		return 0;
+	}
+	while(sh->locked)
+		sleep_on(&sh->wait_queue);
+	sh->locked=1;
+	restore_flags(flags);
+	return 1;
+}
+
+static void shaper_kick(struct shaper *sh);
+
+static void shaper_unlock(struct shaper *sh)
+{
+	sh->locked=0;
+	wake_up(&sh->wait_queue);
+	shaper_kick(sh);
+}
+
+/*
+ *	Compute clocks on a buffer
+ */
+  
+static int shaper_clocks(struct shaper *shaper, struct sk_buff *skb)
+{
+ 	int t=skb->len/shaper->bytespertick;
+ 	return t;
+}
+
+/*
+ *	Set the speed of a shaper. We compute this in bytes per tick since
+ *	thats how the machine wants to run. Quoted input is in bits per second
+ *	as is traditional (note not BAUD). We assume 8 bit bytes. 
+ */
+  
+static void shaper_setspeed(struct shaper *shaper, int bitspersec)
+{
+	shaper->bitspersec=bitspersec;
+	shaper->bytespertick=(bitspersec/HZ)/8;
+	if(!shaper->bytespertick)
+		shaper->bytespertick++;
+}
+
+/*
+ *	Throw a frame at a shaper.
+ */
+  
+static int shaper_qframe(struct shaper *shaper, struct sk_buff *skb)
+{
+ 	struct sk_buff *ptr;
+ 	
+ 	/*
+ 	 *	Get ready to work on this shaper. Lock may fail if its
+ 	 *	an interrupt and locked.
+ 	 */
+ 	 
+ 	if(!shaper_lock(shaper))
+ 		return -1;
+ 	ptr=shaper->sendq.prev;
+ 	
+ 	/*
+ 	 *	Set up our packet details
+ 	 */
+ 	 
+ 	skb->shapelatency=0;
+ 	skb->shapeclock=shaper->recovery;
+ 	if(skb->shapeclock<jiffies)
+ 		skb->shapeclock=jiffies;
+ 	skb->shapestamp=jiffies;
+ 	
+ 	/*
+ 	 *	Time slots for this packet.
+ 	 */
+ 	 
+ 	skb->shapelen= shaper_clocks(shaper,skb);
+ 	
+#ifdef SHAPER_COMPLEX /* and broken.. */
+
+ 	while(ptr && ptr!=(struct sk_buff *)&shaper->sendq)
+ 	{
+ 		if(ptr->pri<skb->pri 
+ 			&& jiffies - ptr->shapeclock < SHAPER_MAXSLIP)
+ 		{
+ 			struct sk_buff *tmp=ptr->prev;
+
+ 			/*
+ 			 *	It goes before us therefore we slip the length
+ 			 *	of the new frame.
+ 			 */
+
+ 			ptr->shapeclock+=skb->shapelen;
+ 			ptr->shapelatency+=skb->shapelen;
+
+ 			/*
+ 			 *	The packet may have slipped so far back it
+ 			 *	fell off.
+ 			 */
+ 			if(ptr->shapelatency > SHAPER_LATENCY)
+ 			{
+ 				skb_unlink(ptr);
+ 				dev_kfree_skb(ptr, FREE_WRITE);
+ 			}
+ 			ptr=tmp;
+ 		}
+ 		else
+ 			break;
+ 	}
+ 	if(ptr==NULL || ptr==(struct sk_buff *)&shaper->sendq)
+ 		skb_queue_head(&shaper->sendq,skb);
+ 	else
+ 	{
+ 		struct sk_buff *tmp;
+ 		/*
+ 		 *	Set the packet clock out time according to the
+ 		 *	frames ahead. Im sure a bit of thought could drop
+ 		 *	this loop.
+ 		 */
+ 		for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && tmp!=ptr; tmp=tmp->next)
+ 			skb->shapeclock+=tmp->shapelen;
+ 		skb_append(ptr,skb);
+ 	}
+#else
+	{
+		struct sk_buff *tmp;
+		/*
+		 *	Up our shape clock by the time pending on the queue
+		 *	(Should keep this in the shaper as a variable..)
+		 */
+		for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && 
+			tmp!=(struct sk_buff *)&shaper->sendq; tmp=tmp->next)
+			skb->shapeclock+=tmp->shapelen;
+		/*
+		 *	Queue over time. Spill packet.
+		 */
+		if(skb->shapeclock-jiffies > SHAPER_LATENCY)
+			dev_kfree_skb(skb, FREE_WRITE);
+		else
+			skb_queue_tail(&shaper->sendq, skb);
+	}
+#endif 	
+ 	if(sh_debug)
+ 		printk("Frame queued.\n");
+ 	if(skb_queue_len(&shaper->sendq)>SHAPER_QLEN)
+ 	{
+ 		ptr=skb_dequeue(&shaper->sendq);
+ 		dev_kfree_skb(ptr, FREE_WRITE);
+ 	}
+ 	shaper_unlock(shaper);
+ 	shaper_kick(shaper);
+ 	return 0;
+}
+
+/*
+ *	Transmit from a shaper
+ */
+ 
+static void shaper_queue_xmit(struct shaper *shaper, struct sk_buff *skb)
+{
+	struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+	if(sh_debug)
+		printk("Kick frame on %p\n",newskb);
+	if(newskb)
+	{
+		newskb->dev=shaper->dev;
+		newskb->arp=1;
+		if(sh_debug)
+			printk("Kick new frame to %s\n",
+				shaper->dev->name);
+		dev_queue_xmit(newskb,shaper->dev,2);
+		if(sh_debug)
+			printk("Kicked new frame out.\n");
+		dev_kfree_skb(skb, FREE_WRITE);
+	}
+}
+
+/*
+ *	Timer handler for shaping clock
+ */
+ 
+static void shaper_timer(unsigned long data)
+{
+	struct shaper *sh=(struct shaper *)data;
+	shaper_kick(sh);
+}
+
+/*
+ *	Kick a shaper queue and try and do something sensible with the 
+ *	queue. 
+ */
+
+static void shaper_kick(struct shaper *shaper)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&shaper->timer);
+
+	/*
+	 *	Shaper unlock will kick
+	 */
+	 
+	if(shaper->locked)
+	{	
+		if(sh_debug)
+			printk("Shaper locked.\n");
+		shaper->timer.expires=jiffies+1;
+		add_timer(&shaper->timer);
+		restore_flags(flags);
+		return;
+	}
+
+		
+	/*
+	 *	Walk the list (may be empty)
+	 */
+	 
+	while((skb=skb_peek(&shaper->sendq))!=NULL)
+	{
+		/*
+		 *	Each packet due to go out by now (within an error
+		 *	of SHAPER_BURST) gets kicked onto the link 
+		 */
+		 
+		if(sh_debug)
+			printk("Clock = %d, jiffies = %ld\n", skb->shapeclock, jiffies);
+		if(skb->shapeclock <= jiffies + SHAPER_BURST)
+		{
+			/*
+			 *	Pull the frame and get interrupts back on.
+			 */
+			 
+			skb_unlink(skb);
+			if (shaper->recovery < skb->shapeclock + skb->shapelen)
+				shaper->recovery = skb->shapeclock + skb->shapelen;
+			restore_flags(flags);
+
+			/*
+			 *	Pass on to the physical target device via
+			 *	our low level packet thrower.
+			 */
+			
+			skb->shapepend=0;
+			shaper_queue_xmit(shaper, skb);	/* Fire */
+			cli();
+		}
+		else
+			break;
+	}
+
+	/*
+	 *	Next kick.
+	 */
+	 
+	if(skb!=NULL)
+	{
+		del_timer(&shaper->timer);
+		shaper->timer.expires=skb->shapeclock;
+		add_timer(&shaper->timer);
+	}
+		
+	/*
+	 *	Interrupts on, mission complete
+	 */
+		
+	restore_flags(flags);
+}
+
+
+/*
+ *	Flush the shaper queues on a closedown
+ */
+ 
+static void shaper_flush(struct shaper *shaper)
+{
+	struct sk_buff *skb;
+	while((skb=skb_dequeue(&shaper->sendq))!=NULL)
+		dev_kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ *	Bring the interface up. We just disallow this until a 
+ *	bind.
+ */
+
+static int shaper_open(struct device *dev)
+{
+	struct shaper *shaper=dev->priv;
+	
+	/*
+	 *	Can't open until attached.
+	 *	Also can't open until speed is set, or we'll get
+	 *	a division by zero.
+	 */
+	 
+	if(shaper->dev==NULL)
+		return -ENODEV;
+	if(shaper->bitspersec==0)
+		return -EINVAL;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Closing a shaper flushes the queues.
+ */
+ 
+static int shaper_close(struct device *dev)
+{
+	struct shaper *shaper=dev->priv;
+	shaper_flush(shaper);
+	del_timer(&shaper->timer);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Revectored calls. We alter the parameters and call the functions
+ *	for our attached device. This enables us to bandwidth allocate after
+ *	ARP and other resolutions and not before.
+ */
+
+
+static int shaper_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct shaper *sh=dev->priv;
+	return shaper_qframe(sh, skb);
+}
+
+static struct enet_statistics *shaper_get_stats(struct device *dev)
+{
+	return NULL;
+}
+
+static int shaper_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+	struct shaper *sh=dev->priv;
+	if(sh_debug)
+		printk("Shaper header\n");
+	return sh->hard_header(skb,sh->dev,type,daddr,saddr,len);
+}
+
+static int shaper_rebuild_header(void *eth, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+	struct shaper *sh=dev->priv;
+	if(sh_debug)
+		printk("Shaper rebuild header\n");
+	return sh->rebuild_header(eth,sh->dev,raddr,skb);
+}
+
+static int shaper_attach(struct device *shdev, struct shaper *sh, struct device *dev)
+{
+	sh->dev = dev;
+	sh->hard_start_xmit=dev->hard_start_xmit;
+	sh->get_stats=dev->get_stats;
+	if(dev->hard_header)
+	{
+		sh->hard_header=dev->hard_header;
+		shdev->hard_header = shaper_header;
+	}
+	else
+		shdev->hard_header = NULL;
+		
+	if(dev->rebuild_header)
+	{
+		sh->rebuild_header	= dev->rebuild_header;
+		shdev->rebuild_header	= shaper_rebuild_header;
+	}
+	else
+		shdev->rebuild_header	= NULL;
+	
+	shdev->hard_header_len=dev->hard_header_len;
+	shdev->type=dev->type;
+	shdev->addr_len=dev->addr_len;
+	shdev->mtu=dev->mtu;
+	sh->bitspersec=0;
+	return 0;
+}
+
+static int shaper_ioctl(struct device *dev,  struct ifreq *ifr, int cmd)
+{
+	struct shaperconf *ss= (struct shaperconf *)&ifr->ifr_data;
+	struct shaper *sh=dev->priv;
+	switch(ss->ss_cmd)
+	{
+		case SHAPER_SET_DEV:
+		{
+			struct device *them=dev_get(ss->ss_name);
+			if(them==NULL)
+				return -ENODEV;
+			if(sh->dev)
+				return -EBUSY;
+			return shaper_attach(dev,dev->priv, them);
+		}
+		case SHAPER_GET_DEV:
+			if(sh->dev==NULL)
+				return -ENODEV;
+			strcpy(ss->ss_name, sh->dev->name);
+			return 0;
+		case SHAPER_SET_SPEED:
+			shaper_setspeed(sh,ss->ss_speed);
+			return 0;
+		case SHAPER_GET_SPEED:
+			ss->ss_speed=sh->bitspersec;
+			return 0;
+		default:
+			return -EINVAL;
+	}
+}
+
+static struct shaper *shaper_alloc(struct device *dev)
+{
+	struct shaper *sh=kmalloc(sizeof(struct shaper), GFP_KERNEL);
+	if(sh==NULL)
+		return NULL;
+	memset(sh,0,sizeof(*sh));
+	skb_queue_head_init(&sh->sendq);
+	init_timer(&sh->timer);
+	sh->timer.function=shaper_timer;
+	sh->timer.data=(unsigned long)sh;
+	return sh;
+}
+
+/*
+ *	Add a shaper device to the system
+ */
+ 
+int shaper_probe(struct device *dev)
+{
+	int i;
+	
+	/*
+	 *	Set up the shaper.
+	 */
+	
+	dev->priv = shaper_alloc(dev);
+	if(dev->priv==NULL)
+		return -ENOMEM;
+		
+	dev->open		= shaper_open;
+	dev->stop		= shaper_close;
+	dev->hard_start_xmit 	= shaper_start_xmit;
+	dev->get_stats 		= shaper_get_stats;
+	dev->set_multicast_list = NULL;
+	
+	/*
+	 *	Intialise the packet queues
+	 */
+	 
+	for(i=0;i<DEV_NUMBUFFS;i++)
+		skb_queue_head_init(&dev->buffs[i]);
+	
+	/*
+	 *	Handlers for when we attach to a device.
+	 */
+
+	dev->hard_header 	= shaper_header;
+	dev->rebuild_header 	= shaper_rebuild_header;
+	dev->do_ioctl		= shaper_ioctl;
+	dev->hard_header_len	= 0;
+	dev->type		= ARPHRD_ETHER;	/* initially */
+	dev->set_mac_address	= NULL;
+	dev->mtu		= 1500;
+	dev->addr_len		= 0;
+	dev->tx_queue_len	= 10;
+	dev->flags		= 0;
+	dev->family		= AF_INET;
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr		= 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= 4;
+		
+	/*
+	 *	Shaper is ok
+	 */	
+	 
+	return 0;
+}
+ 
+#ifdef MODULE
+
+static char devicename[9];
+
+static struct device dev_shape = 
+{
+	devicename,
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+int init_module(void)
+{
+	int i;
+	for(i=0;i<99;i++)
+	{
+		sprintf(devicename,"shaper%d",i);
+		if(dev_get(devicename)==NULL)
+			break;
+	}
+	if(i==100)
+		return -ENFILE;
+	
+	printk(SHAPER_BANNER);	
+	if (register_netdev(&dev_shape) != 0)
+		return -EIO;
+	printk("Traffic shaper initialised.\n");
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	/*
+	 *	No need to check MOD_IN_USE, as sys_delete_module() checks.
+	 *	To be unloadable we must be closed and detached so we don't
+	 *	need to flush things.
+	 */
+	 
+	unregister_netdev(&dev_shape);
+
+	/*
+	 *	Free up the private structure, or leak memory :-) 
+	 */
+	 
+	kfree(dev_shape.priv);
+	dev_shape.priv = NULL;
+}
+
+#else
+
+static struct device dev_sh0 = 
+{
+	"shaper0",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+
+static struct device dev_sh1 = 
+{
+	"shaper1",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+
+static struct device dev_sh2 = 
+{
+	"shaper2",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+static struct device dev_sh3 = 
+{
+	"shaper3",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+void shaper_init(void)
+{
+	register_netdev(&dev_sh0);
+	register_netdev(&dev_sh1);
+	register_netdev(&dev_sh2);
+	register_netdev(&dev_sh3);
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.0.35/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v2.0.35/linux/drivers/net/strip.c	Sun Nov 15 10:49:40 1998
+++ linux/drivers/net/strip.c	Sun Nov 15 10:33:03 1998
@@ -81,8 +81,6 @@
 /************************************************************************/
 /* Header files								*/
 
-#include <linux/config.h>
-
 #ifdef MODULE
 #include <linux/module.h>
 #include <linux/version.h>
@@ -1714,7 +1712,7 @@
 {
     struct strip *strip_info = (struct strip *)(dev->priv);
     STRIP_Header *header = (STRIP_Header *)buff;
-    /* Arp find returns zero if if knows the address, */
+    /* Arp find returns zero if it knows the address, */
     /* or if it doesn't know the address it sends an ARP packet and returns non-zero */
     int arp_result = arp_find(header->dst_addr.c, dst, dev, dev->pa_addr, skb);
 
diff -u --recursive --new-file v2.0.35/linux/drivers/net/tlan.c linux/drivers/net/tlan.c
--- v2.0.35/linux/drivers/net/tlan.c	Sun Nov 15 10:49:40 1998
+++ linux/drivers/net/tlan.c	Sun Nov 15 10:33:04 1998
@@ -6,6 +6,7 @@
  *  by James Banks
  *
  *  (C) 1997-1998 Caldera, Inc.
+ *  (C) 1998 James Banks
  *
  *  This software may be used and distributed according to the terms
  *  of the GNU Public License, incorporated herein by reference.
@@ -58,8 +59,8 @@
 static	int		speed = 0;
 static	u8		*TLanPadBuffer;
 static	char		TLanSignature[] = "TLAN";
-static	int		TLanVersionMajor = 0;
-static	int		TLanVersionMinor = 43;
+static	int		TLanVersionMajor = 1;
+static	int		TLanVersionMinor = 0;
 
 
 static	TLanAdapterEntry TLanAdapterList[] = {
@@ -582,10 +583,7 @@
 
 			if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) {
 				pcibios_write_config_word ( *pci_bus,  *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER );
-				printk( "TLAN:  Attempting to activate busmastering.\n" );
-				printk( "TLAN:    You may need to set busmastering to on in the CMOS\n" );
-				printk( "TLAN:    before this card will work.\n" );
-				*pci_io_base = 0;
+				printk( "TLAN:  Activating PCI bus mastering for this device.\n" );
 			}
 
 			pci_index++;
@@ -1222,7 +1220,7 @@
 	 *	of the list.  If the frame was the last in the Rx
 	 *	channel (EOC), the function restarts the receive channel
 	 *	by sending an Rx Go command to the adapter.  Then it
-	 *	activates/continues the the activity LED.
+	 *	activates/continues the activity LED.
 	 *
 	 **************************************************************/
 
@@ -1723,7 +1721,7 @@
 	 *		io_base		Base IO port of the device of
 	 *				which to print DIO registers.
 	 *
-	 *	This function prints out all the the internal (DIO)
+	 *	This function prints out all the internal (DIO)
 	 *	registers of a TLAN chip.
 	 *
 	 **************************************************************/
diff -u --recursive --new-file v2.0.35/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.0.35/linux/drivers/net/tulip.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/net/tulip.c	Sun Nov 15 10:33:04 1998
@@ -1,12 +1,13 @@
-/* tulip.c: A DEC 21040-family ethernet driver for linux. */
+/* tulip.c: A DEC 21040-family ethernet driver for Linux. */
 /*
 	Written 1994-1998 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 SMC EtherPower PCI ethernet adapter.
-	It should work with most other DEC 21*40-based ethercards.
+	This driver is for the Digital "Tulip" ethernet adapter interface.
+	It should work with most DEC 21*4*-based chips/ethercards, as well as
+	PNIC and MXIC chips.
 
 	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
 	Center of Excellence in Space Data and Information Sciences
@@ -17,7 +18,7 @@
 */
 
 #define SMP_CHECK
-static const char version[] = "tulip.c:v0.88 4/7/98 becker@cesdis.gsfc.nasa.gov\n";
+static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
@@ -30,6 +31,14 @@
 static int options[MAX_UNITS] = {0, };
 static int mtu[MAX_UNITS] = {0, };			/* Jumbo MTU for interfaces. */
 
+/*  The possible media types that can be set in options[] are: */
+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",
+};
+
 /* Set if the PCI BIOS detects the chips on a multiport board backwards. */
 #ifdef REVERSE_PROBE_ORDER
 static int reverse_probe = 1;
@@ -51,18 +60,9 @@
 static int rx_copybreak = 100;
 #endif
 
-/* 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 */
-
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((2000*HZ)/1000)
+#define TX_TIMEOUT  (4*HZ)
 
 #include <linux/config.h>
 #ifdef MODULE
@@ -272,8 +272,6 @@
 #ifndef PCI_VENDOR_ID_LITEON
 #define PCI_VENDOR_ID_LITEON	0x11AD
 #endif
-#define PCI_DEVICE_ID_PNIC		0x0002
-#define PCI_DEVICE_ID_PNIC_X	0x0168
 
 #ifndef PCI_VENDOR_ID_MXIC
 #define	PCI_VENDOR_ID_MXIC		0x10d9
@@ -290,44 +288,44 @@
 static void pnic_timer(unsigned long data);
 
 /* A table describing the chip types. */
-enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2};
+enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,};
 static struct tulip_chip_table {
-	int device_id;
+	int vendor_id, device_id;
 	char *chip_name;
 	int io_size;
 	int valid_intrs;			/* CSR7 interrupt enable settings */
 	int flags;
 	void (*media_timer)(unsigned long data);
 } tulip_tbl[] = {
-  { PCI_DEVICE_ID_DEC_TULIP, "Digital DC21040 Tulip", 128, 0x0001ebef,
-	0, tulip_timer },
-  { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DC21041 Tulip", 128, 0x0001ebef,
-	HAS_MEDIA_TABLE, tulip_timer },
-  { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE, tulip_timer },
-  { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 256, 0x0801fbff,
+  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP,
+	"Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer },
+  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS,
+	"Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer },
+  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST,
+	"Digital DS21140 Tulip", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM,
+	tulip_timer },
+  { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142,
+	"Digital DS21142/3 Tulip", 256, 0x0801fbff,
 	HAS_MII | HAS_MEDIA_TABLE, t21142_timer },
-  { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 256, 0x0001ebef,
-	0, pnic_timer },
-  { PCI_DEVICE_ID_MX98713, "Macronix 98713 PMAC", 128, 0x0001ebef,
-	HAS_MII | HAS_MEDIA_TABLE, tulip_timer /* Tulip-like! */ },
-  { PCI_DEVICE_ID_MX98715, "Macronix 98715 PMAC", 256, 0x0001ebef,
-	HAS_MEDIA_TABLE, mxic_timer },
-  { PCI_DEVICE_ID_MX98725, "Macronix 98725 PMAC", 256, 0x0001ebef,
-	HAS_MEDIA_TABLE, mxic_timer },
+  { PCI_VENDOR_ID_LITEON, 0x0002,
+	"Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer },
+  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713,
+	"Macronix 98713 PMAC", 128, 0x0001ebef,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ },
+  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715,
+	"Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
+  { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725,
+	"Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer },
+  { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff,
+	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
   {0, 0, 0, 0},
 };
 /* This matches the table above. */
 enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
 			 LC82C168, MX98713, MX98715, MX98725};
 
-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. */
+/* A full-duplex map for media types. */
 enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
 		  MediaIs100=16};
 static const char media_cap[] =
@@ -378,7 +376,7 @@
 struct mediatable {
 	u16 defaultmedia;
 	u8 leafcount, csr12dir;				/* General purpose pin directions. */
-	unsigned has_mii:1;
+	unsigned has_mii:1, has_nonmii:1;
 	struct medialeaf mleaf[0];
 };
 
@@ -418,6 +416,7 @@
 	unsigned int tx_full:1;				/* The Tx queue is full. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
 	unsigned int full_duplex_lock:1;
+	unsigned int fake_addr:1;			/* Multiport board faked address. */
 	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. */
@@ -434,12 +433,12 @@
 };
 
 static struct device *tulip_probe1(int pci_bus, int pci_devfn,
-								   struct device *dev, int ioaddr,
+								   struct device *dev,
 								   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 mdio_write(int ioaddr, int phy_id, int location, int value);
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(struct device *dev, int phy_id, int location);
+static void mdio_write(struct device *dev, int phy_id, int location, int value);
 static void select_media(struct device *dev, int startup);
 static int tulip_open(struct device *dev);
 static void tulip_timer(unsigned long data);
@@ -474,98 +473,100 @@
 {
 	int cards_found = 0;
 	static int pci_index = 0;	/* Static, for multiple probe calls. */
+	unsigned char pci_bus, pci_device_fn;
 
 	/* Ideally we would detect all network cards in slot order.  That would
 	   be best done a central PCI probe dispatch, which wouldn't work
 	   well with the current structure.  So instead we detect just the
 	   Tulip cards in slot order. */
 
-	if (pcibios_present()) {
-		unsigned char pci_bus, pci_device_fn;
-
-		for (;pci_index < 0xff; pci_index++) {
-			u16 vendor, device, pci_command, new_command;
-			u32 pci_ioaddr;
-			int 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_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
-				&& vendor != PCI_VENDOR_ID_LITEON
-				&& vendor != PCI_VENDOR_ID_MXIC)
+#if LINUX_VERSION_CODE >= 0x20155
+	if (! pci_present())
+		return -ENODEV;
+#else
+	if (! pcibios_present())
+		return -ENODEV;
+#endif
+	for (;pci_index < 0xff; pci_index++) {
+		u16 vendor, device, pci_command, new_command;
+		u32 pci_ioaddr;
+		int chip_idx = 0;
+
+		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;
-			if (vendor == PCI_VENDOR_ID_LITEON)
-				device = PCI_DEVICE_ID_PNIC_X;
-			else if (vendor == PCI_VENDOR_ID_MXIC)
-				device = PCI_DEVICE_ID_MX98713;
-
-			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) {
+			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);
+
+		for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
+			if (vendor == tulip_tbl[chip_idx].vendor_id  &&
+				device == tulip_tbl[chip_idx].device_id)
+				break;
+		if (tulip_tbl[chip_idx].chip_name == 0) {
+			if (vendor == PCI_VENDOR_ID_DEC  ||
+				vendor == PCI_VENDOR_ID_LITEON)
 				printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type"
 					   " %4.4x %4.4x"" detected: not configured.\n",
 					   vendor, device);
-				continue;
-			}
-			if (tulip_debug > 2)
-				printk(KERN_DEBUG "Found %s at I/O %#x.\n",
-					   tulip_tbl[chip_idx].chip_name, pci_ioaddr);
+			continue;
+		}
+#if LINUX_VERSION_CODE >= 0x20155
+		pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0];
+#else
+		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
+								  &pci_ioaddr);
+#endif
+		/* Remove I/O space marker in bit 0. */
+		pci_ioaddr &= ~3;
 
-			if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
-				continue;
+		if (tulip_debug > 2)
+			printk(KERN_DEBUG "Found %s at I/O %#x.\n",
+				   tulip_tbl[chip_idx].chip_name, pci_ioaddr);
 
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-									 PCI_COMMAND, &pci_command);
-			new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
-			if (pci_command != new_command) {
-				printk(KERN_INFO "  The PCI BIOS has not enabled this"
-					   " device!  Updating PCI command %4.4x->%4.4x.\n",
-					   pci_command, new_command);
-				pcibios_write_config_word(pci_bus, pci_device_fn,
-										  PCI_COMMAND, new_command);
-			}
-
-			dev = tulip_probe1(pci_bus, pci_device_fn, dev,
-							   pci_ioaddr, chip_idx, cards_found);
-
-			/* Get and check the bus-master and latency values. */
-			if (dev) {
-				unsigned char pci_latency;
-				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, "
-						   " PCI command is %4.4x.\n",
-						   pci_latency, new_command);
-				/* Bring the 21143 out power-down mode. */
-				if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
-					pcibios_write_config_dword(pci_bus, pci_device_fn,
-											   0x40, 0x40000000);
-				dev = 0;
-				cards_found++;
-			}
+		if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
+			continue;
+
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+		new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled this"
+				   " device!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
+
+		dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found);
+
+		/* Get and check the bus-master and latency values. */
+		if (dev) {
+			unsigned char pci_latency;
+			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, "
+					   " PCI command is %4.4x.\n",
+					   pci_latency, new_command);
+			/* Bring the 21143 out power-down mode. */
+			if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
+				pcibios_write_config_dword(pci_bus, pci_device_fn,
+										   0x40, 0x40000000);
+			dev = 0;
+			cards_found++;
 		}
 	}
 
@@ -573,15 +574,17 @@
 }
 
 static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
-								   struct device *dev, int ioaddr,
+								   struct device *dev,
 								   int chip_id, int board_idx)
 {
 	static int did_version = 0;			/* Already printed version info. */
 	struct tulip_private *tp;
+	long ioaddr;
+	int irq;
 	/* See note below on the multiport cards. */
 	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
 	static int last_irq = 0;
-	u8 pci_irq_line;
+	static int multiport_cnt = 0;		/* For four-port boards w/one EEPROM */
 	int i;
 	unsigned short sum;
 
@@ -590,9 +593,25 @@
 
 	dev = init_etherdev(dev, 0);
 
-	pcibios_read_config_byte(pci_bus, pci_device_fn,
-							 PCI_INTERRUPT_LINE, &pci_irq_line);
-	printk(KERN_INFO "%s: %s at %#3x,",
+#if LINUX_VERSION_CODE >= 0x20155
+	irq = pci_find_slot(pci_bus, pci_device_fn)->irq;
+	ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0];
+#else
+	{
+		u8 pci_irq_line;
+		u32 pci_ioaddr;
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+								 PCI_INTERRUPT_LINE, &pci_irq_line);
+		pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
+								  &pci_ioaddr);
+		irq = pci_irq_line;
+		ioaddr = pci_ioaddr;
+	}
+#endif
+	/* Remove I/O space marker in bit 0. */
+	ioaddr &= ~3;
+
+	printk(KERN_INFO "%s: %s at %#3lx,",
 		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
 
 	/* Stop the chip's Tx and Rx processes. */
@@ -649,6 +668,10 @@
 		for (i = 0; i < 8; i ++)
 			if (ee_data[i] != ee_data[16+i])
 				sa_offset = 20;
+		if (ee_data[0] == 0xff  &&  ee_data[1] == 0xff &&  ee_data[2] == 0) {
+			sa_offset = 2;		/* Grrr, damn Matrox boards. */
+			multiport_cnt = 4;
+		}
 		for (i = 0; i < 6; i ++) {
 			dev->dev_addr[i] = ee_data[i + sa_offset];
 			sum += ee_data[i + sa_offset];
@@ -672,20 +695,21 @@
 			dev->dev_addr[i] = last_phys_addr[i];
 		dev->dev_addr[i] = last_phys_addr[i] + 1;
 #if defined(__i386__)		/* This BIOS bug doesn't exist on Alphas. */
-		pci_irq_line = last_irq;
+		irq = last_irq;
 #endif
 	}
+
 	for (i = 0; i < 6; i++)
 		printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
-	printk(", IRQ %d.\n", pci_irq_line);
-	last_irq = pci_irq_line;
+	printk(", IRQ %d.\n", irq);
+	last_irq = irq;
 
 	/* We do a request_region() only to register /proc/ioports info. */
 	/* Note that proper size is tulip_tbl[chip_id].chip_name, but... */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
+	request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name);
 
 	dev->base_addr = ioaddr;
-	dev->irq = pci_irq_line;
+	dev->irq = irq;
 
 	/* Make certain the data structures are quadword aligned. */
 	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
@@ -746,9 +770,9 @@
 		   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, 1);
+			int mii_status = mdio_read(dev, phy, 1);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
-				int mii_reg0 = mdio_read(ioaddr, phy, 0);
+				int mii_reg0 = mdio_read(dev, phy, 0);
 				int reg4 = ((mii_status>>6) & tp->to_advertise) | 1;
 				tp->phys[phy_idx] = phy;
 				tp->advertising[phy_idx++] = reg4;
@@ -758,11 +782,11 @@
 				if (1 || (media_cap[tp->default_port] & MediaIsMII)) {
 					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d,"
 						   " previously advertising %4.4x.\n",
-						   dev->name, reg4, phy, mdio_read(ioaddr, phy, 4));
-					mdio_write(ioaddr, phy, 4, reg4);
+						   dev->name, reg4, phy, mdio_read(dev, phy, 4));
+					mdio_write(dev, phy, 4, reg4);
 				}
 				/* Enable autonegotiation: some boards default to off. */
-				mdio_write(ioaddr, phy, 0, mii_reg0 |
+				mdio_write(dev, phy, 0, mii_reg0 |
 						   (tp->full_duplex ? 0x1100 : 0x1000) |
 						   (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
 			}
@@ -813,10 +837,12 @@
 		outl(0x1301, ioaddr + CSR12); /* Start NWay. */
 		break;
 	case LC82C168:
-		outl(0x00420000, ioaddr + CSR6);
-		outl(0x30, ioaddr + CSR12);
-		outl(0x0001F078, ioaddr + 0xB8);
-		outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+		if ( ! tp->mii_cnt) {
+			outl(0x00420000, ioaddr + CSR6);
+			outl(0x30, ioaddr + CSR12);
+			outl(0x0001F078, ioaddr + 0xB8);
+			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+		}
 		break;
 	case MX98713: case MX98715: case MX98725:
 		outl(0x00000000, ioaddr + CSR6);
@@ -846,7 +872,9 @@
   {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
 							   0x0000, 0x009E, /* 10baseT */
 							   0x0903, 0x006D, /* 100baseTx */ }},
-  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f,
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f,
+								 0x0107, 0x8021, /* 100baseFx */
+								 0x0108, 0x8021, /* 100baseFx-FD */
 								 0x0103, 0x006D, /* 100baseTx */ }},
   {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
 							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
@@ -876,7 +904,7 @@
 	static unsigned char *last_ee_data = NULL;
 	static int controller_index = 0;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	unsigned char *ee_data = tp->eeprom;
 	int i;
 
@@ -956,8 +984,9 @@
 		for (i = 0; i < count; i++) {
 			unsigned char media_code = *p++;
 			u16 csrvals[3];
-			for (i = 0; i < 3; i++) {
-				csrvals[i] = get_u16(p);
+			int idx;
+			for (idx = 0; idx < 3; idx++) {
+				csrvals[idx] = get_u16(p);
 				p += 2;
 			}
 			if (media_code & 0x40) {
@@ -977,7 +1006,7 @@
 		u16 media = get_u16(p);
 
 		p += 2;
-		if (tp->chip_id == DC21140  ||  tp->chip_id == MX98713)
+		if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM)
 			csr12dir = *p++;
 		count = *p++;
 		mtable = (struct mediatable *)
@@ -989,7 +1018,7 @@
 		mtable->defaultmedia = media;
 		mtable->leafcount = count;
 		mtable->csr12dir = csr12dir;
-		mtable->has_mii = 0;
+		mtable->has_nonmii = mtable->has_mii = 0;
 
 		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
 			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
@@ -1008,8 +1037,10 @@
 				if (p[1] & 1) {
 					mtable->has_mii = 1;
 					leaf->media = 11;
-				} else
+				} else {
+					mtable->has_nonmii = 1;
 					leaf->media = p[2] & 0x0f;
+				}
 				leaf->leafdata = p + 2;
 				p += (p[0] & 0x3f) + 1;
 			}
@@ -1052,11 +1083,11 @@
 #define EE_READ_CMD		(6 << 6)
 #define EE_ERASE_CMD	(7 << 6)
 
-static int read_eeprom(int ioaddr, int location)
+static int read_eeprom(long ioaddr, int location)
 {
 	int i;
 	unsigned short retval = 0;
-	int ee_addr = ioaddr + CSR9;
+	long ee_addr = ioaddr + CSR9;
 	int read_cmd = location | EE_READ_CMD;
 	
 	outl(EE_ENB & ~EE_CS, ee_addr);
@@ -1107,12 +1138,23 @@
 #define MDIO_ENB_IN		0x40000
 #define MDIO_DATA_READ	0x80000
 
-static int mdio_read(int ioaddr, int phy_id, int location)
+static int mdio_read(struct device *dev, int phy_id, int location)
 {
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
 	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
 	int retval = 0;
-	int mdio_addr = ioaddr + CSR9;
+	long mdio_addr = dev->base_addr + CSR9;
+
+	if (tp->chip_id == LC82C168) {
+		long ioaddr = dev->base_addr;
+		int i = 1000;
+		outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
+		while (--i > 0)
+			if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
+				return retval & 0xffff;
+		return 0xffff;
+	}
 
 	/* Establish sync by sending at least 32 logic ones. */ 
 	for (i = 32; i >= 0; i--) {
@@ -1141,11 +1183,23 @@
 	return (retval>>1) & 0xffff;
 }
 
-static void mdio_write(int ioaddr, int phy_id, int location, int value)
+static void mdio_write(struct device *dev, int phy_id, int location, int value)
 {
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
 	int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
-	int mdio_addr = ioaddr + CSR9;
+	long mdio_addr = dev->base_addr + CSR9;
+
+	if (tp->chip_id == LC82C168) {
+		long ioaddr = dev->base_addr;
+		int i = 1000;
+		outl(cmd, ioaddr + 0xA0);
+		do
+			if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
+				break;
+		while (--i > 0);
+		return;
+	}
 
 	/* Establish sync by sending 32 logic ones. */ 
 	for (i = 32; i >= 0; i--) {
@@ -1177,7 +1231,7 @@
 tulip_open(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int i = 0;
 
 	/* On some chip revs we must set the MII/SYM port before the reset!? */
@@ -1286,11 +1340,11 @@
 		int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 :
 			(dev->if_port == 12 ? 0 : dev->if_port);
 		for (i = 0; i < tp->mtable->leafcount; i++)
-		  if (tp->mtable->mleaf[i].media == looking_for) {
-			printk(KERN_INFO "%s: Using user-specified media %s.\n",
-				   dev->name, medianame[dev->if_port]);
-			goto media_picked;
-		  }
+			if (tp->mtable->mleaf[i].media == looking_for) {
+				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++)
@@ -1313,6 +1367,10 @@
 		outl(0x0008, ioaddr + CSR15);
 		outl(0x0001, ioaddr + CSR13);
 		outl(0x1301, ioaddr + CSR12);
+	} else if (tp->chip_id == LC82C168  &&  tp->mii_cnt && ! tp->medialock) {
+		dev->if_port = 11;
+		tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0);
+		outl(0x0001, ioaddr + CSR15);
 	} else
 		select_media(dev, 1);
 
@@ -1325,14 +1383,15 @@
 	dev->start = 1;
 
 	/* Enable interrupts by setting the interrupt mask. */
+	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
 	outl(tulip_tbl[tp->chip_id].valid_intrs, 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",
+		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n",
 			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
-			   inl(ioaddr + CSR13));
+			   inl(ioaddr + CSR6));
 	}
 	/* Set the timer to switch to check for link beat and perhaps switch
 	   to an alternate media type. */
@@ -1348,7 +1407,7 @@
 /* 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;
+	long ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	struct mediatable *mtable = tp->mtable;
 	u32 new_csr6;
@@ -1441,7 +1500,7 @@
 				printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d (%d).\n",
 					   dev->name, to_advertise, phy_num, tp->phys[phy_num]);
 			/* Bogus: put in by a committee?  */
-			mdio_write(ioaddr, tp->phys[phy_num], 4, to_advertise);
+			mdio_write(dev, tp->phys[phy_num], 4, to_advertise);
 			break;
 		}
 		default:
@@ -1462,14 +1521,18 @@
 		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
 		new_csr6 = 0x80020000;
 	} else if (tp->chip_id == LC82C168) {
-		if (startup)
-			dev->if_port = 0;
+		if (startup && ! tp->medialock)
+			dev->if_port = tp->mii_cnt ? 11 : 0;
 		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x,"
+			printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x,"
 				   " media %s.\n",
 				   dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
 				   medianame[dev->if_port]);
-		if (startup) {
+		if (tp->mii_cnt) {
+			new_csr6 = 0x812C0000;
+			outl(0x0001, ioaddr + CSR15);
+			outl(0x0201B07A, ioaddr + 0xB8);
+		} else if (startup) {
 			/* Start with 10mbps to do autonegotiation. */
 			outl(0x32, ioaddr + CSR12);
 			new_csr6 = 0x00420000;
@@ -1525,7 +1588,7 @@
 {
 	struct device *dev = (struct device *)data;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	u32 csr12 = inl(ioaddr + CSR12);
 	int next_tick = 0;
 
@@ -1661,22 +1724,22 @@
 		case 1:  case 3: {		/* 21140, 21142 MII */
 			int mii_reg1, mii_reg5;
 		actually_mii:
-			mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
-			mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
+			mii_reg1 = mdio_read(dev, tp->phys[0], 1);
+			mii_reg5 = mdio_read(dev, tp->phys[0], 5);
 			if (tulip_debug > 1)
 				printk(KERN_INFO "%s: MII status %4.4x, Link partner report "
 					   "%4.4x, CSR12 %2.2x, %cD.\n",
 					   dev->name, mii_reg1, mii_reg5, csr12,
 					   tp->full_duplex ? 'F' : 'H');
 			if (mii_reg1 != 0xffff  &&  (mii_reg1 & 0x0004) == 0) {
-				int new_reg1 = mdio_read(ioaddr, tp->phys[0], 1);
-				if ((new_reg1 & 0x0004) == 0)
+				int new_reg1 = mdio_read(dev, tp->phys[0], 1);
+				if ((new_reg1 & 0x0004) == 0) {
 					printk(KERN_INFO "%s: No link beat on the MII interface,"
 						   " status then %4.4x now %4.4x.\n",
 						   dev->name, mii_reg1, new_reg1);
-#ifdef notyet
-				goto select_next_media;
-#endif
+					if (tp->mtable  &&  tp->mtable->has_nonmii)
+						goto select_next_media;
+				}
 			}
 			if (mii_reg5 == 0xffff  ||  mii_reg5 == 0x0000)
 				;				/* No MII device or no link partner report */
@@ -1724,7 +1787,7 @@
 {
 	struct device *dev = (struct device *)data;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int csr12 = inl(ioaddr + CSR12);
 	int next_tick = 60*HZ;
 	int new_csr6 = 0;
@@ -1788,7 +1851,7 @@
 static void t21142_lnk_change( struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int csr12 = inl(ioaddr + CSR12);
 
 	if (tulip_debug > 1)
@@ -1839,7 +1902,7 @@
 {
 	struct device *dev = (struct device *)data;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int next_tick = 60*HZ;
 
 	if (tulip_debug > 3) {
@@ -1856,50 +1919,82 @@
 {
 	struct device *dev = (struct device *)data;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int csr12 = inl(ioaddr + CSR12);
-	int phy_reg = inl(ioaddr + 0xB8);
 	int next_tick = 60*HZ;
-	int duplex;
+	int new_csr6 = tp->csr6 & ~0x40C40200;
 
-	if (tulip_debug > 1)
-		printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
-			   dev->name, phy_reg, inl(ioaddr + CSR5));
-	if (tp->full_duplex_lock)
-		return;				/* Do not bother to set timer. */
-	duplex = phy_reg & 0x30000000 ? 1 : 0;
-	if (tp->full_duplex != duplex) {
-		tp->full_duplex = duplex;
-		if (tp->full_duplex)
-			tp->csr6 |= 0x0200;
+	if (media_cap[dev->if_port] & MediaIsMII) {
+		int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0];
+
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: LC82C168 negotiated capability %8.8x, "
+				   "CSR5 %8.8x.\n",
+				   dev->name, negotiated, inl(ioaddr + CSR5));
+
+		if (negotiated & 0x0380) 				/* 10 vs 100mbps */
+			new_csr6 |= 0x812E0000;
 		else
-			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"
-				   " PNIC PHY report of %8.8x.\n",
-				   dev->name, tp->full_duplex ? "full" : "half", phy_reg);
-	}
-	if (phy_reg & 0x04000000) {	/* Remote link fault */
-		/*outl(0x0201F078, ioaddr + 0xB8);*/
-		next_tick = 3*HZ;
-	}
-	if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+			new_csr6 |= 0x816E0000;
+		if (((negotiated & 0x0300) == 0x0100)			/* Duplex */
+			|| (negotiated & 0x00C0) == 0x0040
+			|| tp->full_duplex_lock) {
+			tp->full_duplex = 1;
+			new_csr6 |= 0x0200;
+		}
 		if (tulip_debug > 1)
-			printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
-				   "CSR5 %8.8x, PHY %3.3x.\n",
-				   dev->name, medianame[dev->if_port], csr12,
-				   inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
-		if (dev->if_port == 0) {
-			dev->if_port = 3;
+			printk(KERN_DEBUG "%s: LC82C168 MII PHY status %4.4x, Link "
+				   "partner report %4.4x, csr6 %8.8x/%8.8x.\n",
+			   dev->name, mdio_read(dev, tp->phys[0], 1), negotiated,
+				   tp->csr6, inl(ioaddr + CSR6));
+	} else {
+		int phy_reg = inl(ioaddr + 0xB8);
+		int csr5 = inl(ioaddr + CSR5);
+
+		if (tulip_debug > 1)
+			printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
+				   dev->name, phy_reg, csr5);
+
+		if (phy_reg & 0x04000000) {	/* Remote link fault */
+			/*outl(0x0201F078, ioaddr + 0xB8);*/
+			next_tick = 3*HZ;
+		}
+		if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */
+			if (tulip_debug > 1)
+				printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, "
+					   "CSR5 %8.8x, PHY %3.3x.\n",
+					   dev->name, medianame[dev->if_port], csr12,
+					   inl(ioaddr + CSR5), inl(ioaddr + 0xB8));
+			if (tp->medialock) {
+			} else if (dev->if_port == 0) {
+				dev->if_port = 3;
+				outl(0x33, ioaddr + CSR12);
+				new_csr6 = 0x01860000;
+				outl(0x1F868, ioaddr + 0xB8);
+			} else {
+				dev->if_port = 0;
+				outl(0x32, ioaddr + CSR12);
+				new_csr6 = 0x00420000;
+				outl(0x1F078, ioaddr + 0xB8);
+			}
+			new_csr6 |= (tp->csr6 & 0xfdff);
+			next_tick = 3*HZ;
 		} else
-			dev->if_port = 0;
-		next_tick = 3*HZ;
-		select_media(dev, 0);
-		outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+			new_csr6 = tp->csr6;
+		if (tp->full_duplex_lock  ||  (phy_reg & 0x30000000) != 0) {
+			tp->full_duplex = 1;
+			new_csr6 |= 0x00000200;
+		}
+	}
+	if (tp->csr6 != new_csr6) {
+		tp->csr6 = new_csr6;
+		outl(tp->csr6 | 0x0002, ioaddr + CSR6);	/* Restart Tx */
 		outl(tp->csr6 | 0x2002, ioaddr + CSR6);
 		dev->trans_start = jiffies;
+		if (tulip_debug > 0) /* Gurppp, should be >1 */
+			printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
+				   "CSR6 %8.8x.\n",
+				   dev->name, tp->full_duplex ? "full" : "half", new_csr6);
 	}
 	tp->timer.expires = RUN_AT(next_tick);
 	add_timer(&tp->timer);
@@ -1908,13 +2003,15 @@
 static void tulip_tx_timeout(struct device *dev)
 {
   struct tulip_private *tp = (struct tulip_private *)dev->priv;
-  int ioaddr = dev->base_addr;
+  long 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);
+  if (media_cap[dev->if_port] & MediaIsMII) {
+	  /* Do nothing -- the media monitor should handle this. */
+	  if (tulip_debug > 1)
+		  printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
+				 dev->name);
+	  dev->trans_start = jiffies;
+	  return;
   } else if (tp->chip_id == DC21040) {
 	  if (inl(ioaddr + CSR12) & 0x0002) {
 		  printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n",
@@ -2102,7 +2199,8 @@
 #endif
 
 	struct tulip_private *tp;
-	int csr5, ioaddr, work_budget = max_interrupt_work;
+	long ioaddr;
+	int csr5, work_budget = max_interrupt_work;
 
 	if (dev == NULL) {
 		printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n",
@@ -2119,8 +2217,8 @@
 			   tp->smp_proc_id, hard_smp_processor_id());
 #else
 		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
-		return;
 #endif
+		return;
 	}
 	dev->interrupt = 1;
 #ifdef SMP_CHECK
@@ -2210,6 +2308,14 @@
 			}
 
 			tp->dirty_tx = dirty_tx;
+			if (csr5 & TxDied) {
+				if (tulip_debug > 1)
+					printk(KERN_WARNING "%s: The transmitter stopped!"
+						   "  CSR5 is %x, CSR6 %x.\n",
+						   dev->name, csr5, inl(ioaddr + CSR6));
+				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			}
 		}
 
 		/* Log errors. */
@@ -2261,24 +2367,6 @@
 		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
 			   dev->name, inl(ioaddr + CSR5));
 
-#ifdef notdef
-	/* Code that should never be run!  Perhaps remove after testing.. */
-	{
-		static int stopit = 10;
-		if (dev->start == 0  &&  --stopit < 0) {
-			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
-		}
-	}
-#endif
-
 	dev->interrupt = 0;
 	clear_bit(0, (void*)&tp->interrupt);
 	return;
@@ -2311,6 +2399,9 @@
 			}
 		} else if (status & 0x8000) {
 			/* There was a fatal error. */
+			if (tulip_debug > 2)
+				printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
+					   dev->name, status);
 			tp->stats.rx_errors++; /* end of a packet.*/
 			if (status & 0x0890) tp->stats.rx_length_errors++;
 			if (status & 0x0004) tp->stats.rx_frame_errors++;
@@ -2318,7 +2409,7 @@
 			if (status & 0x0001) tp->stats.rx_fifo_errors++;
 		} else {
 			/* Omit the four octet CRC from the length. */
-			short pkt_len = (status >> 16) - 4;
+			short pkt_len = ((status >> 16) & 0x7FF) - 4;
 			struct sk_buff *skb;
 
 			/* Check if the packet is long enough to just accept without
@@ -2396,7 +2487,7 @@
 static int
 tulip_close(struct device *dev)
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int i;
 
@@ -2464,7 +2555,7 @@
 tulip_get_stats(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 
 	if (dev->start)
 		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
@@ -2514,7 +2605,7 @@
 		} else {
 			save_flags(flags);
 			cli();
-			data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+			data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
 			restore_flags(flags);
 		}
 		return 0;
@@ -2525,7 +2616,7 @@
 		} else {
 			save_flags(flags);
 			cli();
-			mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+			mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
 			restore_flags(flags);
 		}
 		return 0;
@@ -2569,7 +2660,7 @@
 static void set_rx_mode(struct device *dev, int num_addrs, void *addrs)
 #endif
 {
-	int ioaddr = dev->base_addr;
+	long ioaddr = dev->base_addr;
 	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
@@ -2686,7 +2777,7 @@
 	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io);
 	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
 	io &= ~3;
-	dev = tulip_probe1(bus, devfn, NULL, io, DC21142, -1);
+	dev = tulip_probe1(bus, devfn, NULL, DC21142, -1);
 	if (dev) {
 		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
 		strcpy(node->dev_name, dev->name);
diff -u --recursive --new-file v2.0.35/linux/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- v2.0.35/linux/drivers/net/via-rhine.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/via-rhine.c	Sun Nov 15 10:33:04 1998
@@ -0,0 +1,1317 @@
+/* via-rhine.c: A Linux Ethernet device driver for VIA Rhine family chips. */
+/*
+	Written 1998 by Donald Becker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License (GPL), incorporated herein by reference.
+	Drivers derived from this code also fall under the GPL and must retain
+	this authorship and copyright notice.
+
+	This driver is designed for the VIA VT86c100A Rhine-II PCI Fast Ethernet
+	controller.  It also works with the older 3043 Rhine-I chip.
+
+	The author may be reached as becker@cesdis.edu, or
+	Donald Becker
+	312 Severn Ave. #W302
+	Annapolis MD 21403
+
+	Support and updates available at
+	http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html
+*/
+
+static const char *versionA =
+"via-rhine.c:v1.00 9/5/98  Written by Donald Becker\n";
+static const char *versionB =
+"  http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n";
+
+/* A few user-configurable values.   These may be modified when a driver
+   module is loaded.*/
+
+static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
+static int max_interrupt_work = 20;
+static int min_pci_latency = 64;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak = 0;
+
+/* Used to pass the media type, etc.
+   Both 'options[]' and 'full_duplex[]' should exist for driver
+   interoperability.
+   The media type is usually passed in 'options[]'.
+*/
+#define MAX_UNITS 8		/* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+   The Rhine has a 64 element 8390-like hash table.  */
+static const int multicast_filter_limit = 32;
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+   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	8
+#define RX_RING_SIZE	16
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (2*HZ)
+
+#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+
+/* Include files, designed to support most kernel versions 2.0.0 and later. */
+#include <linux/config.h>
+#include <linux/version.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/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+/* This driver was written to use PCI memory space, however some boards
+   only work with I/O space accesses. */
+#define VIA_USE_IO
+#ifdef VIA_USE_IO
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Kernel compatibility defines, some common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+
+#define RUN_AT(x) (jiffies + (x))
+
+#if (LINUX_VERSION_CODE >= 0x20100)
+char kernel_version[] = UTS_RELEASE;
+#else
+#ifndef __alpha__
+#define ioremap vremap
+#define iounmap vfree
+#endif
+#endif
+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(min_pci_latency, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+#endif
+#if LINUX_VERSION_CODE < 0x20123
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+#if LINUX_VERSION_CODE <= 0x20139
+#define	net_device_stats enet_statistics
+#else
+#define NETSTATS_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
+/* Grrrr, the PCI code changed, but did not consider CardBus... */
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb);
+#endif
+
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet
+controller.
+
+II. Board-specific settings
+
+Boards with this chip are functional only in a bus-master PCI slot.
+
+Many operational settings are loaded from the EEPROM to the Config word at
+offset 0x78.  This driver assumes that they are correct.
+If this driver is compiled to use PCI memory space operations the EEPROM
+must be configured to enable memory ops.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver attempts to use a zero-copy receive and transmit scheme.
+
+Alas, all data buffers are required to start on a 32 bit boundary, so
+the driver must often copy transmit packets into bounce buffers.
+
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip 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.  Buffers consumed this way are replaced by newly allocated
+skbuffs in the last phase of netdev_rx().
+
+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.  New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets.  When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine.  Copying also preloads the cache, which is
+most useful with small frames.
+
+Since the VIA chips are only able to transfer data to buffers on 32 bit
+boundaries, the the IP header at offset 14 in an ethernet frame isn't
+longword aligned for further processing.  Copying these unaligned buffers
+has the beneficial effect of 16-byte aligning the IP header.
+
+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 interrupt handling 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 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring.  After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+IVb. References
+
+Preliminary VT86C100A manual from http://www.via.com.tw/
+http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+
+IVc. Errata
+
+The VT86C100A manual is not reliable information.
+The chip does not handle unaligned transmit or receive buffers, resulting
+in significant performance degradation for bounce buffer copies on transmit
+and unaligned IP headers on receive.
+The chip does not pad to minimum transmit length.
+
+*/
+
+
+
+/* This table drives the PCI probe routines.  It's mostly boilerplate in all
+   of the drivers, and will likely be provided by some future kernel.
+   Note the matching code -- the first table entry matchs all 56** cards but
+   second only the 1234 card.
+*/
+enum pci_flags_bit {
+	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+	PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+};
+struct pci_id_info {
+	const char *name;
+	u16	vendor_id, device_id, device_id_mask, flags;
+	int io_size;
+	struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+							 long ioaddr, int irq, int chip_idx, int fnd_cnt);
+};
+
+static struct device *via_probe1(int pci_bus, int pci_devfn,
+								 struct device *dev, long ioaddr, int irq,
+								 int chp_idx, int fnd_cnt);
+
+static struct pci_id_info pci_tbl[] = {
+	{ "VIA VT86C100A Rhine-II", 0x1106, 0x6100, 0xffff,
+	  PCI_USES_MEM|PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
+	{ "VIA VT3043 Rhine", 0x1106, 0x3043, 0xffff,
+	  PCI_USES_IO|PCI_USES_MEM|PCI_USES_MASTER, 128, via_probe1},
+	{0,},						/* 0 terminated list. */
+};
+
+
+/* A chip capabilities table, matching the entries in pci_tbl[] above. */
+enum chip_capability_flags {CanHaveMII=1, };
+struct chip_info {
+	int io_size;
+	int flags;
+} static cap_tbl[] = {
+	{128, CanHaveMII, },
+	{128, CanHaveMII, },
+};
+
+
+/* Offsets to the device registers.
+*/
+enum register_offsets {
+	StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08,
+	IntrStatus=0x0C, IntrEnable=0x0E,
+	MulticastFilter0=0x10, MulticastFilter1=0x14,
+	RxRingPtr=0x18, TxRingPtr=0x1C,
+	MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIConfig=0x6E,
+	MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72,
+	Config=0x78, RxMissed=0x7C, RxCRCErrs=0x7E,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+	IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020,
+	IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010,
+	IntrPCIErr=0x0040,
+	IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200,
+	IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
+	IntrTxAborted=0x2000, IntrLinkChange=0x4000,
+	IntrRxWakeUp=0x8000,
+	IntrNormalSummary=0x0003, IntrAbnormalSummary=0x8260,
+};
+
+
+/* The Rx and Tx buffer descriptors. */
+struct rx_desc {
+	u16 rx_status;
+	u16 rx_length;
+	u32 desc_length;
+	u32 addr;
+	u32 next_desc;
+};
+struct tx_desc {
+	u16 tx_status;
+	u16 tx_own;
+	u32 desc_length;
+	u32 addr;
+	u32 next_desc;
+};
+
+/* Bits in *_desc.status */
+enum rx_status_bits {
+	RxDescOwn=0x80000000, RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F};
+enum desc_status_bits {
+	DescOwn=0x8000, DescEndPacket=0x4000, DescIntr=0x1000,
+};
+
+/* Bits in ChipCmd. */
+enum chip_cmd_bits {
+	CmdInit=0x0001, CmdStart=0x0002, CmdStop=0x0004, CmdRxOn=0x0008,
+	CmdTxOn=0x0010, CmdTxDemand=0x0020, CmdRxDemand=0x0040,
+	CmdEarlyRx=0x0100, CmdEarlyTx=0x0200, CmdFDuplex=0x0400,
+	CmdNoTxPoll=0x0800, CmdReset=0x8000,
+};
+
+struct netdev_private {
+	/* Descriptor rings first for alignment. */
+	struct rx_desc rx_ring[RX_RING_SIZE];
+	struct tx_desc tx_ring[TX_RING_SIZE];
+	/* The addresses of receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	/* The saved address of a sent-in-place packet/buffer, for later free(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	unsigned char *tx_buf[TX_RING_SIZE];	/* Tx bounce buffers */
+	unsigned char *tx_bufs;				/* Tx bounce buffer region. */
+	struct device *next_module;			/* Link for devices of this type. */
+	struct net_device_stats stats;
+	struct timer_list timer;	/* Media monitoring timer. */
+	unsigned char pci_bus, pci_devfn;
+	/* Frequently used values: keep some adjacent for cache effect. */
+	int chip_id;
+	long in_interrupt;			/* Word-long for SMP locks. */
+	struct rx_desc *rx_head_desc;
+	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
+	unsigned int cur_tx, dirty_tx;
+	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
+	u16 chip_cmd;						/* Current setting for ChipCmd */
+	unsigned int tx_full:1;				/* The Tx queue is full. */
+	/* These values are keep track of the transceiver/media in use. */
+	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int duplex_lock:1;
+	unsigned int medialock:1;			/* Do not sense media. */
+	unsigned int default_port:4;		/* Last dev->if_port value. */
+	u8 tx_thresh, rx_thresh;
+	/* MII transceiver section. */
+	int mii_cnt;						/* MII device addresses. */
+	u16 advertising;					/* NWay media advertisement */
+	unsigned char phys[2];				/* MII device addresses. */
+};
+
+static int  mdio_read(struct device *dev, int phy_id, int location);
+static void mdio_write(struct device *dev, int phy_id, int location, int value);
+static int  netdev_open(struct device *dev);
+static void check_duplex(struct device *dev);
+static void netdev_timer(unsigned long data);
+static void tx_timeout(struct device *dev);
+static void init_ring(struct device *dev);
+static int  start_tx(struct sk_buff *skb, struct device *dev);
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static int  netdev_rx(struct device *dev);
+static void netdev_error(struct device *dev, int intr_status);
+static void set_rx_mode(struct device *dev);
+static struct net_device_stats *get_stats(struct device *dev);
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static int  netdev_close(struct device *dev);
+
+
+
+/* A list of our installed devices, for removing the driver module. */
+static struct device *root_net_dev = NULL;
+
+/* Ideally we would detect all network cards in slot order.  That would
+   be best done a central PCI probe dispatch, which wouldn't work
+   well when dynamically adding drivers.  So instead we detect just the
+   cards we know about in slot order. */
+
+static int pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[])
+{
+	int cards_found = 0;
+	int pci_index = 0;
+	unsigned char pci_bus, pci_device_fn;
+
+	if ( ! pcibios_present())
+		return -ENODEV;
+
+	for (;pci_index < 0xff; pci_index++) {
+		u16 vendor, device, pci_command, new_command;
+		int chip_idx, irq;
+		long pciaddr;
+		long ioaddr;
+
+		if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
+								&pci_bus, &pci_device_fn)
+			!= PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_VENDOR_ID, &vendor);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_DEVICE_ID, &device);
+
+		for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+			if (vendor == pci_tbl[chip_idx].vendor_id
+				&& (device & pci_tbl[chip_idx].device_id_mask) ==
+				pci_tbl[chip_idx].device_id)
+				break;
+		if (pci_tbl[chip_idx].vendor_id == 0) 		/* Compiled out! */
+			continue;
+
+		{
+#if defined(PCI_SUPPORT_VER2)
+			struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+#ifdef VIA_USE_IO
+			pciaddr = pdev->base_address[0];
+#else
+			pciaddr = pdev->base_address[1];
+#endif
+			irq = pdev->irq;
+#else
+			u32 pci_memaddr;
+			u8 pci_irq_line;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+#ifdef VIA_USE_IO
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_memaddr);
+			pciaddr = pci_memaddr;
+#else
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_1, &pci_memaddr);
+			pciaddr = pci_memaddr;
+#endif
+			irq = pci_irq_line;
+#endif
+		}
+
+		if (debug > 2)
+			printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n",
+				   pci_tbl[chip_idx].name, pciaddr, irq);
+
+		if (pci_tbl[chip_idx].flags & PCI_USES_IO) {
+			if (check_region(pciaddr, pci_tbl[chip_idx].io_size))
+				continue;
+			ioaddr = pciaddr & ~3;
+		} else if ((ioaddr = (long)ioremap(pciaddr & ~0xf,
+										 pci_tbl[chip_idx].io_size)) == 0) {
+			printk(KERN_INFO "Failed to map PCI address %#lx.\n",
+				   pciaddr);
+			continue;
+		}
+
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+		new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled the"
+				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_bus, pci_device_fn, pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
+
+		dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr,
+									   irq, chip_idx, cards_found);
+
+		if (dev  && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
+			u8 pci_latency;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency < min_pci_latency) {
+				printk(KERN_INFO "  PCI latency timer (CFLT) is "
+					   "unreasonably low at %d.  Setting to %d clocks.\n",
+					   pci_latency, min_pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, min_pci_latency);
+			}
+		}
+		dev = 0;
+		cards_found++;
+	}
+
+	return cards_found ? 0 : -ENODEV;
+}
+
+#ifndef MODULE
+int via_rhine_probe(struct device *dev)
+{
+	return pci_etherdev_probe(dev, pci_tbl);
+}
+#endif
+
+static struct device *via_probe1(int pci_bus, int pci_devfn,
+								 struct device *dev, long ioaddr, int irq,
+								 int chip_id, int card_idx)
+{
+	static int did_version = 0;		/* Already printed version info */
+	struct netdev_private *np;
+	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+
+	if (debug > 0 && did_version++ == 0)
+		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+
+	dev = init_etherdev(dev, 0);
+
+	printk(KERN_INFO "%s: %s at 0x%lx, ",
+		   dev->name, pci_tbl[chip_id].name, ioaddr);
+
+	/* Ideally we would be read the EEPROM but access may be locked. */
+	for (i = 0; i <6; i++)
+		dev->dev_addr[i] = readb(ioaddr + StationAddr + i);
+	for (i = 0; i < 5; i++)
+			printk("%2.2x:", dev->dev_addr[i]);
+	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#ifdef VIA_USE_IO
+	request_region(ioaddr, pci_tbl[chip_id].io_size, dev->name);
+#endif
+
+	/* Reset the chip to erase previous misconfiguration. */
+	writew(CmdReset, ioaddr + ChipCmd);
+
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+
+	/* Make certain the descriptor lists are cache-aligned. */
+	np = (void *)(((long)kmalloc(sizeof(*np), GFP_KERNEL) + 31) & ~31);
+	memset(np, 0, sizeof(*np));
+	dev->priv = np;
+
+	np->next_module = root_net_dev;
+	root_net_dev = dev;
+
+	np->pci_bus = pci_bus;
+	np->pci_devfn = pci_devfn;
+	np->chip_id = chip_id;
+
+	if (dev->mem_start)
+		option = dev->mem_start;
+
+	/* The lower four bits are the media type. */
+	if (option > 0) {
+		if (option & 0x200)
+			np->full_duplex = 1;
+		np->default_port = option & 15;
+		if (np->default_port)
+			np->medialock = 1;
+	}
+	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0)
+		np->full_duplex = 1;
+
+	if (np->full_duplex)
+		np->duplex_lock = 1;
+
+	/* The chip-specific entries in the device structure. */
+	dev->open = &netdev_open;
+	dev->hard_start_xmit = &start_tx;
+	dev->stop = &netdev_close;
+	dev->get_stats = &get_stats;
+	dev->set_multicast_list = &set_rx_mode;
+	dev->do_ioctl = &mii_ioctl;
+
+	if (cap_tbl[np->chip_id].flags & CanHaveMII) {
+		int phy, phy_idx = 0;
+		np->phys[0] = 1;		/* Standard for this chip. */
+		for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+			int mii_status = mdio_read(dev, phy, 1);
+			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+				np->phys[phy_idx++] = phy;
+				np->advertising = mdio_read(dev, phy, 4);
+				printk(KERN_INFO "%s: MII PHY found at address %d, status "
+					   "0x%4.4x advertising %4.4x Link %4.4x.\n",
+					   dev->name, phy, mii_status, np->advertising,
+					   mdio_read(dev, phy, 5));
+			}
+		}
+		np->mii_cnt = phy_idx;
+	}
+
+	return dev;
+}
+
+
+/* Read and write over the MII Management Data I/O (MDIO) interface. */
+
+static int mdio_read(struct device *dev, int phy_id, int regnum)
+{
+	long ioaddr = dev->base_addr;
+	int boguscnt = 1024;
+
+	/* Wait for a previous command to complete. */
+	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0)
+		;
+	writeb(0x00, ioaddr + MIICmd);
+	writeb(phy_id, ioaddr + MIIPhyAddr);
+	writeb(regnum, ioaddr + MIIRegAddr);
+	writeb(0x40, ioaddr + MIICmd);			/* Trigger read */
+	boguscnt = 1024;
+	while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0)
+		;
+	return readw(ioaddr + MIIData);
+}
+
+static void mdio_write(struct device *dev, int phy_id, int regnum, int value)
+{
+	long ioaddr = dev->base_addr;
+	int boguscnt = 1024;
+
+	/* Wait for a previous command to complete. */
+	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0)
+		;
+	writeb(0x00, ioaddr + MIICmd);
+	writeb(phy_id, ioaddr + MIIPhyAddr);
+	writeb(regnum, ioaddr + MIIRegAddr);
+	writew(value, ioaddr + MIIData);
+	writeb(0x20, ioaddr + MIICmd);			/* Trigger write. */
+	return;
+}
+
+
+static int netdev_open(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int i;
+
+	/* Reset the chip. */
+	writew(CmdReset, ioaddr + ChipCmd);
+
+	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
+			   dev->name, dev->irq);
+
+	MOD_INC_USE_COUNT;
+
+	init_ring(dev);
+
+	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
+	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
+
+	for (i = 0; i < 6; i++)
+		writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+	/* Initialize other registers. */
+	writew(0x0006, ioaddr + PCIConfig);	/* Tune configuration??? */
+	/* Configure the FIFO thresholds. */
+	writeb(0x20, ioaddr + TxConfig);	/* Initial threshold 32 bytes */
+	np->tx_thresh = 0x20;
+	np->rx_thresh = 0x60;				/* Written in set_rx_mode(). */
+
+	if (dev->if_port == 0)
+		dev->if_port = np->default_port;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	np->in_interrupt = 0;
+
+	set_rx_mode(dev);
+
+	dev->start = 1;
+
+	/* Enable interrupts by setting the interrupt mask. */
+	writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped|
+		   IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+		   IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange,
+		   ioaddr + IntrEnable);
+
+	np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll;
+	writew(np->chip_cmd, ioaddr + ChipCmd);
+
+	check_duplex(dev);
+
+	if (debug > 2)
+		printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x "
+			   "MII status: %4.4x.\n",
+			   dev->name, readw(ioaddr + ChipCmd),
+			   mdio_read(dev, np->phys[0], 1));
+
+	/* Set the timer to check for link beat. */
+	init_timer(&np->timer);
+	np->timer.expires = RUN_AT(1);
+	np->timer.data = (unsigned long)dev;
+	np->timer.function = &netdev_timer;				/* timer handler */
+	add_timer(&np->timer);
+
+	return 0;
+}
+
+static void check_duplex(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+	int duplex;
+
+	if (np->duplex_lock  ||  mii_reg5 == 0xffff)
+		return;
+	duplex = (mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040;
+	if (np->full_duplex != duplex) {
+		np->full_duplex = duplex;
+		if (debug)
+			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
+				   " partner capability of %4.4x.\n", dev->name,
+				   duplex ? "full" : "half", np->phys[0], mii_reg5);
+		if (duplex)
+			np->chip_cmd |= CmdFDuplex;
+		else
+			np->chip_cmd &= ~CmdFDuplex;
+		writew(np->chip_cmd, ioaddr + ChipCmd);
+	}
+}
+
+static void netdev_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	int next_tick = 10*HZ;
+
+	if (debug > 3) {
+		printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n",
+			   dev->name, readw(ioaddr + IntrStatus));
+	}
+	check_duplex(dev);
+
+	np->timer.expires = RUN_AT(next_tick);
+	add_timer(&np->timer);
+}
+
+static void tx_timeout(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status "
+		   "%4.4x, resetting...\n",
+		   dev->name, readw(ioaddr + IntrStatus),
+		   mdio_read(dev, np->phys[0], 1));
+
+  /* Perhaps we should reinitialize the hardware here. */
+  dev->if_port = 0;
+  /* Stop and restart the chip's Tx processes . */
+
+  /* Trigger an immediate transmit demand. */
+
+  dev->trans_start = jiffies;
+  np->stats.tx_errors++;
+  return;
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	int i;
+
+	np->tx_full = 0;
+	np->cur_rx = np->cur_tx = 0;
+	np->dirty_rx = np->dirty_tx = 0;
+
+	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+	np->rx_head_desc = &np->rx_ring[0];
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		np->rx_ring[i].rx_status = 0;
+		np->rx_ring[i].rx_length = 0;
+		np->rx_ring[i].desc_length = np->rx_buf_sz;
+		np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]);
+		np->rx_skbuff[i] = 0;
+	}
+	/* Mark the last entry as wrapping the ring. */
+	np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]);
+
+	/* Fill in the Rx buffers. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
+		np->rx_skbuff[i] = skb;
+		if (skb == NULL)
+			break;
+		skb->dev = dev;			/* Mark as being used by this device. */
+		np->rx_ring[i].addr = virt_to_bus(skb->tail);
+		np->rx_ring[i].rx_status = 0;
+		np->rx_ring[i].rx_length = DescOwn;
+	}
+	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		np->tx_skbuff[i] = 0;
+		np->tx_ring[i].tx_own = 0;
+		np->tx_ring[i].desc_length = 0x00e08000;
+		np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]);
+		np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL);
+	}
+	np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]);
+
+	return;
+}
+
+static int start_tx(struct sk_buff *skb, struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	unsigned entry;
+
+	/* 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 (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start < TX_TIMEOUT)
+			return 1;
+		tx_timeout(dev);
+		return 1;
+	}
+
+	/* Caution: the write order is important here, set the field
+	   with the "ownership" bits last. */
+
+	/* Calculate the next Tx descriptor entry. */
+	entry = np->cur_tx % TX_RING_SIZE;
+
+	np->tx_skbuff[entry] = skb;
+
+	if ((long)skb->data & 3) {			/* Must use alignment buffer. */
+		if (np->tx_buf[entry] == NULL &&
+			(np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL)
+			return 1;
+		memcpy(np->tx_buf[entry], skb->data, skb->len);
+		np->tx_ring[entry].addr = virt_to_bus(np->tx_buf[entry]);
+	} else
+		np->tx_ring[entry].addr = virt_to_bus(skb->data);
+
+	np->tx_ring[entry].desc_length = 0x00E08000 |
+		(skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN);
+	np->tx_ring[entry].tx_own = DescOwn;
+
+	np->cur_tx++;
+
+	/* Non-x86 Todo: explicitly flush cache lines here. */
+
+	/* Wake the potentially-idle transmit channel. */
+	writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+
+	if (np->cur_tx - np->dirty_tx < TX_RING_SIZE - 1)
+		clear_bit(0, (void*)&dev->tbusy);		/* Typical path */
+	else
+		np->tx_full = 1;
+	dev->trans_start = jiffies;
+
+	if (debug > 4) {
+		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
+			   dev->name, np->cur_tx, entry);
+	}
+	return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+	struct device *dev = (struct device *)dev_instance;
+	struct netdev_private *np;
+	long ioaddr, boguscnt = max_interrupt_work;
+
+	ioaddr = dev->base_addr;
+	np = (struct netdev_private *)dev->priv;
+#if defined(__i386__)
+	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+			   dev->name);
+		dev->interrupt = 0;	/* Avoid halting machine. */
+		return;
+	}
+#else
+	if (dev->interrupt) {
+		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
+		return;
+	}
+	dev->interrupt = 1;
+#endif
+
+	do {
+		u32 intr_status = readw(ioaddr + IntrStatus);
+
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		writew(intr_status & 0xffff, ioaddr + IntrStatus);
+
+		if (debug > 4)
+			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+				   dev->name, intr_status);
+
+		if (intr_status == 0)
+			break;
+
+		if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped |
+						   IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
+			netdev_rx(dev);
+
+		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
+			int entry = np->dirty_tx % TX_RING_SIZE;
+			int txstatus;
+			if (np->tx_ring[entry].tx_own)
+				break;
+			txstatus = np->tx_ring[entry].tx_status;
+			if (debug > 6)
+				printk(KERN_DEBUG " Tx scavenge %d status %4.4x.\n",
+					   entry, txstatus);
+			if (txstatus & 0x8000) {
+				if (debug > 1)
+					printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n",
+						   dev->name, txstatus);
+				np->stats.tx_errors++;
+				if (txstatus & 0x0400) np->stats.tx_carrier_errors++;
+				if (txstatus & 0x0200) np->stats.tx_window_errors++;
+				if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
+				if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++;
+				if (txstatus & 0x0002) np->stats.tx_fifo_errors++;
+#ifdef ETHER_STATS
+				if (txstatus & 0x0100) np->stats.collisions16++;
+#endif
+				/* Transmitter restarted in 'abnormal' handler. */
+			} else {
+#ifdef ETHER_STATS
+				if (txstatus & 0x0001) np->stats.tx_deferred++;
+#endif
+				np->stats.collisions += (txstatus >> 3) & 15;
+#if defined(NETSTATS_VER2)
+				np->stats.tx_bytes += np->tx_ring[entry].desc_length & 0x7ff;
+#endif
+				np->stats.tx_packets++;
+			}
+			/* Free the original skb. */
+			dev_free_skb(np->tx_skbuff[entry]);
+			np->tx_skbuff[entry] = 0;
+		}
+		if (np->tx_full && dev->tbusy
+			&& np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) {
+			/* The ring is no longer full, clear tbusy. */
+			np->tx_full = 0;
+			clear_bit(0, (void*)&dev->tbusy);
+			mark_bh(NET_BH);
+		}
+
+		/* Abnormal error summary/uncommon events handlers. */
+		if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
+						   IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
+			netdev_error(dev, intr_status);
+
+		if (--boguscnt < 0) {
+			printk(KERN_WARNING "%s: Too much work at interrupt, "
+				   "status=0x%4.4x.\n",
+				   dev->name, intr_status);
+			break;
+		}
+	} while (1);
+
+	if (debug > 3)
+		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+			   dev->name, readw(ioaddr + IntrStatus));
+
+#if defined(__i386__)
+	clear_bit(0, (void*)&dev->interrupt);
+#else
+	dev->interrupt = 0;
+#endif
+	return;
+}
+
+/* This routine is logically part of the interrupt handler, but isolated
+   for clarity and better register allocation. */
+static int netdev_rx(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	int entry = np->cur_rx % RX_RING_SIZE;
+	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+
+	if (debug > 4) {
+		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
+			   entry, np->rx_head_desc->rx_length);
+	}
+
+	/* If EOP is set on the next entry, it's a new packet. Send it up. */
+	while ( ! (np->rx_head_desc->rx_length & DescOwn)) {
+		struct rx_desc *desc = np->rx_head_desc;
+		int data_size = desc->rx_length;
+		u16 desc_status = desc->rx_status;
+
+		if (debug > 4)
+			printk(KERN_DEBUG "  netdev_rx() status is %4.4x.\n",
+				   desc_status);
+		if (--boguscnt < 0)
+			break;
+		if ( (desc_status & (RxWholePkt | RxErr)) !=  RxWholePkt) {
+			if ((desc_status & RxWholePkt) !=  RxWholePkt) {
+				printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+					   "multiple buffers, entry %#x length %d status %4.4x!\n",
+					   dev->name, np->cur_rx, data_size, desc_status);
+				printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n",
+					   dev->name, np->rx_head_desc,
+					   &np->rx_ring[np->cur_rx % RX_RING_SIZE]);
+				np->stats.rx_length_errors++;
+			} else if (desc_status & RxErr) {
+				/* There was a error. */
+				if (debug > 2)
+					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n",
+						   desc_status);
+				np->stats.rx_errors++;
+				if (desc_status & 0x0030) np->stats.rx_length_errors++;
+				if (desc_status & 0x0048) np->stats.rx_fifo_errors++;
+				if (desc_status & 0x0004) np->stats.rx_frame_errors++;
+				if (desc_status & 0x0002) np->stats.rx_crc_errors++;
+			}
+		} else {
+			struct sk_buff *skb;
+			/* Length should omit the CRC */
+			u16 pkt_len = data_size - 4;
+
+			/* Check if the packet is long enough to accept without copying
+			   to a minimally-sized skbuff. */
+			if (pkt_len < rx_copybreak
+				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	/* 16 byte align the IP header */
+#if ! defined(__alpha__) || USE_IP_COPYSUM		/* Avoid misaligned on Alpha */
+				eth_copy_and_sum(skb, bus_to_virt(desc->addr),
+								 pkt_len, 0);
+				skb_put(skb, pkt_len);
+#else
+				memcpy(skb_put(skb,pkt_len), bus_to_virt(desc->addr), pkt_len);
+#endif
+			} else {
+				skb_put(skb = np->rx_skbuff[entry], pkt_len);
+				np->rx_skbuff[entry] = NULL;
+			}
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+			np->stats.rx_packets++;
+		}
+		entry = (++np->cur_rx) % RX_RING_SIZE;
+		np->rx_head_desc = &np->rx_ring[entry];
+	}
+
+	/* Refill the Rx ring buffers. */
+	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+		struct sk_buff *skb;
+		entry = np->dirty_rx % RX_RING_SIZE;
+		if (np->rx_skbuff[entry] == NULL) {
+			skb = dev_alloc_skb(np->rx_buf_sz);
+			np->rx_skbuff[entry] = skb;
+			if (skb == NULL)
+				break;			/* Better luck next round. */
+			skb->dev = dev;			/* Mark as being used by this device. */
+			np->rx_ring[entry].addr = virt_to_bus(skb->tail);
+		}
+		np->rx_ring[entry].rx_status = 0;
+		np->rx_ring[entry].rx_length = DescOwn;
+	}
+
+	/* Pre-emptively restart Rx engine. */
+	writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+	return 0;
+}
+
+static void netdev_error(struct device *dev, int intr_status)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	if (intr_status & (IntrMIIChange | IntrLinkChange)) {
+		if (readb(ioaddr + MIIStatus) & 0x02)
+			/* Link failed, restart autonegotiation. */
+			mdio_write(dev, np->phys[0], 0, 0x3300);
+		else
+			check_duplex(dev);
+		if (debug)
+			printk(KERN_ERR "%s: MII status changed: Autonegotiation "
+				   "advertising %4.4x  partner %4.4x.\n", dev->name,
+			   mdio_read(dev, np->phys[0], 4),
+			   mdio_read(dev, np->phys[0], 5));
+	}
+	if (intr_status & IntrStatsMax) {
+		np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs);
+		np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed);
+		writel(0, RxMissed);
+	}
+	if (intr_status & IntrTxAbort) {
+		/* Stats counted in Tx-done handler, just restart Tx. */
+		writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+	}
+	if (intr_status & IntrTxUnderrun) {
+		if (np->tx_thresh < 0xE0)
+			writeb(np->tx_thresh += 0x20, ioaddr + TxConfig);
+		if (debug > 1)
+			printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
+				   "threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
+	}
+	if ((intr_status & ~(IntrLinkChange|IntrStatsMax|IntrTxAbort)) && debug) {
+		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
+			   dev->name, intr_status);
+		/* Recovery for other fault sources not known. */
+		writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+	}
+}
+
+static struct enet_statistics *get_stats(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+
+	/* Nominally we should lock this segment of code for SMP, although
+	   the vulnerability window is very small and statistics are
+	   non-critical. */
+	np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs);
+	np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed);
+	writel(0, RxMissed);
+
+	return &np->stats;
+}
+
+/* The big-endian AUTODIN II ethernet CRC calculation.
+   N.B. Do not use for bulk data, use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+    int crc = -1;
+
+    while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
+			crc = (crc << 1) ^
+				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+		}
+    }
+    return crc;
+}
+
+static void set_rx_mode(struct device *dev)
+{
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	long ioaddr = dev->base_addr;
+	u32 mc_filter[2];			/* Multicast hash filter */
+	u8 rx_mode;					/* Note: 0x02=accept runt, 0x01=accept errs */
+
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		/* Unconditionally log net taps. */
+		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+		rx_mode = 0x1C;
+	} else if ((dev->mc_count > multicast_filter_limit)
+			   ||  (dev->flags & IFF_ALLMULTI)) {
+		/* Too many to match, or accept all multicasts. */
+		rx_mode = 0x0C;
+	} else {
+		struct dev_mc_list *mclist;
+		int i;
+		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(ETH_ALEN, mclist->dmi_addr) >> 26,
+					mc_filter);
+		}
+		writel(mc_filter[0], ioaddr + MulticastFilter0);
+		writel(mc_filter[1], ioaddr + MulticastFilter1);
+		rx_mode = 0x0C;
+	}
+	writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig);
+}
+
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	u16 *data = (u16 *)&rq->ifr_data;
+
+	switch(cmd) {
+	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+		data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
+		/* Fall Through */
+	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+		return 0;
+	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+		if (!suser())
+			return -EPERM;
+		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int netdev_close(struct device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct netdev_private *np = (struct netdev_private *)dev->priv;
+	int i;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (debug > 1)
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+			   dev->name, readw(ioaddr + ChipCmd));
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	writew(0x0000, ioaddr + IntrEnable);
+
+	/* Stop the chip's Tx and Rx processes. */
+	writew(CmdStop, ioaddr + ChipCmd);
+
+	del_timer(&np->timer);
+
+	free_irq(dev->irq, dev);
+
+	/* Free all the skbuffs in the Rx queue. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		np->rx_ring[i].rx_length = 0;
+		np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
+		if (np->rx_skbuff[i]) {
+#if LINUX_VERSION_CODE < 0x20100
+			np->rx_skbuff[i]->free = 1;
+#endif
+			dev_free_skb(np->rx_skbuff[i]);
+		}
+		np->rx_skbuff[i] = 0;
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (np->tx_skbuff[i])
+			dev_free_skb(np->tx_skbuff[i]);
+		np->tx_skbuff[i] = 0;
+	}
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	if (debug)					/* Emit version even if no cards detected. */
+		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+#ifdef CARDBUS
+	register_driver(&etherdev_ops);
+	return 0;
+#else
+	return pci_etherdev_probe(NULL, pci_tbl);
+#endif
+}
+
+void cleanup_module(void)
+{
+
+#ifdef CARDBUS
+	unregister_driver(&etherdev_ops);
+#endif
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (root_net_dev) {
+		struct netdev_private *np =
+			(struct netdev_private *)(root_net_dev->priv);
+		unregister_netdev(root_net_dev);
+#ifdef VIA_USE_IO
+		release_region(root_net_dev->base_addr, pci_tbl[np->chip_id].io_size);
+#else
+		iounmap((char *)(root_net_dev->base_addr));
+#endif
+		kfree(root_net_dev);
+		root_net_dev = np->next_module;
+#if 0
+		kfree(np);				/* Assumption: no struct realignment. */
+#endif
+	}
+}
+
+#endif  /* MODULE */
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c via-rhine.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v2.0.35/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.0.35/linux/drivers/net/wavelan.c	Sun Nov 15 10:49:41 1998
+++ linux/drivers/net/wavelan.c	Sun Nov 15 10:33:04 1998
@@ -2967,8 +2967,8 @@
   /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
    * (does it work for everybody? -- especially old cards?) */
   /* Note: WFREQSEL verifies that it is able to read a sensible
-   * frequency from from EEPROM (address 0x00) and that
-   * MMR_FEE_STATUS_ID is 0xA (Xilinx version) or 0xB (Ariadne version).
+   * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID
+   * is 0xA (Xilinx version) or 0xB (Ariadne version).
    * My test is more crude but does work. */
   if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
        (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
diff -u --recursive --new-file v2.0.35/linux/drivers/net/wic.c linux/drivers/net/wic.c
--- v2.0.35/linux/drivers/net/wic.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/wic.c	Wed Dec 31 16:00:00 1969
@@ -1,1459 +0,0 @@
-/* $Id: wic.c,v 1.3.2.1 1997/03/09 02:14:40 davem Exp $ */
-/* WIC: A parallel port "network" driver for Linux. */
-/* based on the plip network driver */
-/* Modified for Linux 1.3.x by Alan Cox <Alan.Cox@linux.org> */
-
-char *version = "NET3 WIC version 0.9 hayes@netplumbing.com";
-
-/*
-  Sources:
-	Ideas and protocols came from Russ Nelson's <nelson@crynwr.com>
-	"parallel.asm" parallel port packet driver and from the plip.c
-	parallel networking linux driver from the 1.2.13 Linux
-	distribution.
-
-  The packet is encapsulated as if it were ethernet.
-
-*/
-
-#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>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/string.h>
-#include <linux/ptrace.h>
-#include <linux/if_ether.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <linux/in.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/lp.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_wic.h>
-
-#include <linux/tqueue.h>
-#include <linux/ioport.h>
-#include <asm/bitops.h>
-#include <asm/irq.h>
-#include <asm/byteorder.h>
-#include <linux/string.h>
-
-#define NET_DEBUG 1
-/* Use 0 for production, 1 for verification, >2 for debug */
-#ifndef NET_DEBUG
-#define NET_DEBUG 1
-#endif
-unsigned int net_debug = NET_DEBUG;
-
-/* Connection time out = WIC_TRIGGER_WAIT * WIC_DELAY_UNIT usec */
-#define WIC_TRIGGER_WAIT	 500
-
-/* Nibble time out = WIC_NIBBLE_WAIT * WIC_DELAY_UNIT usec */
-#define WIC_NIBBLE_WAIT        3000
-
-#define PAR_DATA(dev)		((dev)->base_addr+0)
-#define PAR_STATUS(dev)		((dev)->base_addr+1)
-#define PAR_CONTROL(dev)	((dev)->base_addr+2)
-
-/* Bottom halfs */
-void wic_kick_bh(struct device *dev);
-void wic_bh(struct device *dev);
-
-/* Interrupt handler */
-void wic_interrupt(int irq, void *dev_ptr, struct pt_regs *regs);
-
-/* Functions for DEV methods */
-int wic_rebuild_header(void *buff, struct device *dev,
-			       unsigned long raddr, struct sk_buff *skb);
-int wic_tx_packet(struct sk_buff *skb, struct device *dev);
-int wic_open(struct device *dev);
-int wic_close(struct device *dev);
-struct enet_statistics *wic_get_stats(struct device *dev);
-int wic_config(struct device *dev, struct ifmap *map);
-int wic_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-int send_cmd(struct device *dev, unsigned char *cmd, char len);
-int recv_cmd_resp(struct device *dev, unsigned char *cmd);
-int send_byte(struct device *dev, unsigned char c);
-int get_byte(struct device *dev, unsigned char *c);
-int ack_resp(struct device *dev);
-int check_bfr(struct device *dev);
-void wic_reset(struct device *dev);
-void wic_set_multicast_list(struct device *dev);
-
-#define LOOPCNT 30000	
-unsigned char tog = 3;
-unsigned char save = 0;
-
-enum wic_connection_state {
-	WIC_CN_NONE=0,
-	WIC_CN_RECEIVE,
-	WIC_CN_SEND,
-	WIC_CN_CLOSING,
-	WIC_CN_ERROR
-};
-
-enum wic_packet_state {
-	WIC_PK_DONE=0,
-	WIC_PK_TRIGGER,
-	WIC_PK_LENGTH_LSB,
-	WIC_PK_LENGTH_MSB,
-	WIC_PK_DATA,
-	WIC_PK_CHECKSUM
-};
-
-enum wic_nibble_state {
-	WIC_NB_BEGIN,
-	WIC_NB_1,
-	WIC_NB_2,
-};
-
-struct wic_local {
-	enum wic_packet_state state;
-	enum wic_nibble_state nibble;
-	union {
-		struct {
-#if defined(__LITTLE_ENDIAN)
-			unsigned char lsb;
-			unsigned char msb;
-#elif defined(__BIG_ENDIAN)
-			unsigned char msb;
-			unsigned char lsb;
-#else
-#error	"Please fix the endianness defines in <asm/byteorder.h>"
-#endif						
-		} b;
-		unsigned short h;
-	} length;
-	unsigned short byte;
-	unsigned char  checksum;
-	unsigned char  data;
-	struct sk_buff *skb;
-};
-
-struct net_local {
-	struct enet_statistics enet_stats;
-	struct tq_struct immediate;
-	struct tq_struct deferred;
-	struct wic_local snd_data;
-	struct wic_local rcv_data;
-	unsigned long  trigger;
-	unsigned long  nibble;
-	enum wic_connection_state connection;
-	unsigned short timeout_count;
-	char is_deferred;
-	int (*orig_rebuild_header)(void *eth, struct device *dev,
-				   unsigned long raddr, struct sk_buff *skb);
-};
-
-/* Entry point of WIC driver.
-   Probe the hardware, and register/initialize the driver. */
-int
-wic_init(struct device *dev)
-{
-	struct net_local *nl;
-	struct wicconf wc;
-	int i;
-
-	/* Check region before the probe */
-	if (check_region(PAR_DATA(dev), 3) < 0)
-		return -ENODEV;
-	
-	/* Check that there is something at base_addr. */
-	outb(0, PAR_DATA(dev));
-	udelay(1000);
-	if (inb(PAR_DATA(dev)) != 0)
-		return -ENODEV;
-
-	printk("%s\n",version);
-	printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
-	if (dev->irq) {
-		printk("using assigned IRQ %d.\n", dev->irq);
-	} else {
-		int irq = 0;
-#ifdef MODULE
-		/* dev->irq==0 means autoprobe, but we don't try to do so
-		   with module.  We can change it by ifconfig */
-#else
-		unsigned int irqs = probe_irq_on();
-
-		outb(0x00, PAR_CONTROL(dev));
-		udelay(1000);
-		udelay(1000);
-		irq = probe_irq_off(irqs);
-#endif
-		if (irq > 0) {
-			dev->irq = irq;
-			printk("using probed IRQ %d.\n", dev->irq);
-		} else
-			printk("failed to detect IRQ(%d) --"
-			       " Please set IRQ by ifconfig.\n", irq);
-	}
-
-	request_region(PAR_DATA(dev), 3, dev->name);
-
-	/* Fill in the generic fields of the device structure. */
-	ether_setup(dev);
-
-	/* Then, override parts of it */
-	dev->hard_start_xmit	= wic_tx_packet;
-	dev->open		= wic_open;
-	dev->stop		= wic_close;
-	dev->get_stats 		= wic_get_stats;
-	dev->set_config		= wic_config;
-	dev->do_ioctl		= wic_ioctl;
-	dev->mtu		= 1514;
-	dev->set_multicast_list = wic_set_multicast_list;
-	dev->flags	        = IFF_BROADCAST | IFF_RUNNING | IFF_NOTRAILERS;
-
-	/* get the MAC address from the controller */
-	wc.len = 1;
-	wc.pcmd = WIC_GETNET;
-	check_bfr(dev);
-	send_cmd(dev, (unsigned char *)&wc, 1);
-	wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
-	while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */
-		wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
-	
-	printk("%s:MAC address: ",dev->name);	
-	for (i=0; i < ETH_ALEN ; i++) {
-		dev->dev_addr[i] = wc.data[i];
-		printk("%2x ",dev->dev_addr[i]);
-	}
-	printk("\n");
-
-	/* Set the private structure */
-	dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
-	if (dev->priv == NULL)
-		return EAGAIN;
-	memset(dev->priv, 0, sizeof(struct net_local));
-	nl = (struct net_local *) dev->priv;
-
-	nl->orig_rebuild_header = dev->rebuild_header;
-	dev->rebuild_header 	= wic_rebuild_header;
-
-	/* Initialize constants */
-	nl->trigger	= WIC_TRIGGER_WAIT;
-	nl->nibble	= WIC_NIBBLE_WAIT;
-
-	/* Initialize task queue structures */
-	nl->immediate.next = NULL;
-	nl->immediate.sync = 0;
-	nl->immediate.routine = (void *)(void *)wic_bh;
-	nl->immediate.data = dev;
-
-	nl->deferred.next = NULL;
-	nl->deferred.sync = 0;
-	nl->deferred.routine = (void *)(void *)wic_kick_bh;
-	nl->deferred.data = dev;
-
-	return 0;
-}
-
-/* Bottom half handler for the delayed request.
-   This routine is kicked by do_timer().
-   Request `wic_bh' to be invoked. */
-void
-wic_kick_bh(struct device *dev)
-{
-	struct net_local *nl = (struct net_local *)dev->priv;
-
-	if (nl->is_deferred) {
-		queue_task(&nl->immediate, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
-	}
-}
-
-/* Forward declarations of internal routines */
-int wic_none(struct device *, struct net_local *,
-		     struct wic_local *, struct wic_local *);
-int wic_receive_packet(struct device *, struct net_local *,
-			       struct wic_local *, struct wic_local *);
-int wic_send_packet(struct device *, struct net_local *,
-			    struct wic_local *, struct wic_local *);
-int wic_connection_close(struct device *, struct net_local *,
-				 struct wic_local *, struct wic_local *);
-int wic_error(struct device *, struct net_local *,
-		      struct wic_local *, struct wic_local *);
-int wic_bh_timeout_error(struct device *dev, struct net_local *nl,
-				 struct wic_local *snd,
-				 struct wic_local *rcv,
-				 int error);
-
-#define OK        0
-#define TIMEOUT   1
-#define ERROR     2
-
-typedef int (*wic_func)(struct device *dev, struct net_local *nl,
-			 struct wic_local *snd, struct wic_local *rcv);
-
-wic_func connection_state_table[] =
-{
-	wic_none,
-	wic_receive_packet,
-	wic_send_packet,
-	wic_connection_close,
-	wic_error
-};
-
-void 
-wic_set_multicast_list(struct device *dev)
-{
-	struct wicconf wc;
-	struct wic_net *wn;
-	
-	disable_irq(dev->irq);
-	save &= 0xef; /* disable */
-	outb(save, PAR_CONTROL(dev));
-	
-	wc.len = 1;
-	wc.pcmd = WIC_GETNET;
-	check_bfr(dev);
-	tog = 3;
-	send_cmd(dev, (unsigned char *)&wc, 1);
-	wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
-	while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */
-		wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
-	wn = (struct wic_net *)&wc.data;
-	if(dev->flags&IFF_PROMISC)
-	{
-		/* promiscuous mode */
-		wn->mode |= (NET_MODE_ME | NET_MODE_BCAST | 
-			NET_MODE_MCAST | NET_MODE_PROM);
-		printk("%s: Setting promiscuous mode\n", dev->name);
-	}
-	else if((dev->flags&IFF_ALLMULTI) || dev->mc_count)
-	{
-		wn->mode &= ~NET_MODE_PROM;
-		wn->mode |= (NET_MODE_MCAST | NET_MODE_ME | NET_MODE_BCAST);
-	}
-	else
-	{
-		wn->mode &= ~(NET_MODE_PROM | NET_MODE_MCAST);
-		wn->mode |= (NET_MODE_ME | NET_MODE_BCAST);
-	}
-	wc.len = 23;
-	wc.pcmd = WIC_SETNET;
-	check_bfr(dev);
-	tog = 3;
-	send_cmd(dev, (unsigned char *)&wc, wc.len);
-
-	save |= 0x10; /* enable */
-	outb(save, PAR_CONTROL(dev));
-	enable_irq(dev->irq);
-	return;
-}
-
-/* Bottom half handler of WIC. */
-void
-wic_bh(struct device *dev)
-{
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct wic_local *snd = &nl->snd_data;
-	struct wic_local *rcv = &nl->rcv_data;
-	wic_func f;
-	int r;
-
-	nl->is_deferred = 0;
-	f = connection_state_table[nl->connection];
-	if ((r = (*f)(dev, nl, snd, rcv)) != OK
-	    && (r = wic_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
-		nl->is_deferred = 1;
-		queue_task(&nl->deferred, &tq_timer);
-	}
-}
-
-int
-wic_bh_timeout_error(struct device *dev, struct net_local *nl,
-		      struct wic_local *snd, struct wic_local *rcv,
-		      int error)
-{
-	unsigned char c0;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	if (nl->connection == WIC_CN_SEND) {
-
-		if (error != ERROR) { /* Timeout */
-			nl->timeout_count++;
-			if ((snd->state == WIC_PK_TRIGGER
-			     && nl->timeout_count <= 10)
-			    || nl->timeout_count <= 3) {
-				restore_flags(flags);
-				/* Try again later */
-				return TIMEOUT;
-			}
-			c0 = inb(PAR_STATUS(dev));
-			printk("%s: transmit timeout(%d,%02x)\n",
-			       dev->name, snd->state, c0);
-		}
-		nl->enet_stats.tx_errors++;
-		nl->enet_stats.tx_aborted_errors++;
-	} else if (nl->connection == WIC_CN_RECEIVE) {
-		if (rcv->state == WIC_PK_TRIGGER) {
-			/* Transmission was interrupted. */
-			restore_flags(flags);
-			return OK;
-		}
-		if (error != ERROR) { /* Timeout */
-			if (++nl->timeout_count <= 3) {
-				restore_flags(flags);
-				/* Try again later */
-				return TIMEOUT;
-			}
-			c0 = inb(PAR_STATUS(dev));
-			printk("%s: receive timeout(%d,%02x)\n",
-			       dev->name, rcv->state, c0);
-		}
-		nl->enet_stats.rx_dropped++;
-	}
-	rcv->state = WIC_PK_DONE;
-	if (rcv->skb) {
-		rcv->skb->free = 1;
-		kfree_skb(rcv->skb, FREE_READ);
-		rcv->skb = NULL;
-	}
-	snd->state = WIC_PK_DONE;
-	if (snd->skb) {
-		snd->skb->free = 1;
-		dev_kfree_skb(snd->skb, FREE_WRITE);
-		snd->skb = NULL;
-	}
-#if (0)
-	disable_irq(dev->irq);
-	save &= 0xef; /* disable */
-	outb(save, PAR_CONTROL(dev));
-	dev->tbusy = 1;
-	outb(0x00, PAR_DATA(dev));
-#endif /* (0) */
-	nl->connection = WIC_CN_ERROR;
-	restore_flags(flags);
-
-	return TIMEOUT;
-}
-
-int
-wic_none(struct device *dev, struct net_local *nl,
-	  struct wic_local *snd, struct wic_local *rcv)
-{
-	return OK;
-}
-
-/* WIC_RECEIVE --- receive a byte(two nibbles)
-   Returns OK on success, TIMEOUT on timeout */
-inline int
-wic_receive(unsigned short nibble_timeout, unsigned short status_addr,
-	     enum wic_nibble_state *ns_p, unsigned char *data_p)
-{
-unsigned int cx;
-
-	cx = LOOPCNT;
-	while ((inb(status_addr) & 0x08) != ((tog<<3) & 0x08)) {
-		if (--cx == 0) {
-			return TIMEOUT;
-		}
-	}
-	*data_p = inb(status_addr-1);
-	tog ^= 0x01;
-	outb(tog| save, status_addr+1);
-	return OK;
-}
-
-/* WIC_RECEIVE_PACKET --- receive a packet */
-int
-wic_receive_packet(struct device *dev, struct net_local *nl,
-		    struct wic_local *snd, struct wic_local *rcv)
-{
-	unsigned short status_addr = PAR_STATUS(dev);
-	unsigned short nibble_timeout = nl->nibble;
-	unsigned char *lbuf;
-	unsigned char junk;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	switch (rcv->state) {
-	case WIC_PK_TRIGGER:
-		disable_irq(dev->irq);
-		save &= 0xef; /* disable */
-		outb(save, PAR_CONTROL(dev));
-		
-		dev->interrupt = 0;
-
-		tog &= 0xfe;
-		ack_resp(dev);
-		if (net_debug > 2)
-			printk("%s: receive start\n", dev->name);
-		rcv->state = WIC_PK_LENGTH_LSB;
-		rcv->nibble = WIC_NB_BEGIN;
-
-	case WIC_PK_LENGTH_LSB:
-		if (net_debug > 2)
-			printk("%s: WIC_PK_LENGTH_LSB\n", dev->name);
-		if (snd->state != WIC_PK_DONE) {
-			if (wic_receive(nl->trigger, status_addr,
-					 &rcv->nibble, &rcv->length.b.lsb)) {
-				/* collision, here dev->tbusy == 1 */
-				rcv->state = WIC_PK_DONE;
-				nl->is_deferred = 1;
-				nl->connection = WIC_CN_SEND;
-				restore_flags(flags);
-				queue_task(&nl->deferred, &tq_timer);
-				save |= 0x10; /* enable */
-				outb(save, PAR_CONTROL(dev));
-				enable_irq(dev->irq);
-				return OK;
-			}
-		} else {
-			if (wic_receive(nibble_timeout, status_addr,
-					 &rcv->nibble, &rcv->length.b.lsb)) {
-				restore_flags(flags);
-				return TIMEOUT;
-			}
-		}
-		rcv->state = WIC_PK_LENGTH_MSB;
-
-	case WIC_PK_LENGTH_MSB:
-		if (net_debug > 2)
-			printk("%s: WIC_PK_LENGTH_MSB\n", dev->name);
-		if (wic_receive(nibble_timeout, status_addr,
-				 &rcv->nibble, &rcv->length.b.msb)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}
-		if (rcv->length.h > dev->mtu || rcv->length.h < 8) {
-			printk("%s: bad packet size %d.\n", dev->name, rcv->length.h);
-			restore_flags(flags);
-			return ERROR;
-		}
-		/* Malloc up new buffer. */
-		rcv->skb = dev_alloc_skb(rcv->length.h);
-		if (rcv->skb == NULL) {
-			printk("%s: Memory squeeze.\n", dev->name);
-			restore_flags(flags);
-			return ERROR;
-		}
-		skb_put(rcv->skb,rcv->length.h);
-		rcv->skb->dev = dev;
-		
-		rcv->state = WIC_PK_DATA;
-		rcv->byte = 0;
-		rcv->checksum = 0;
-		
-		/* sequence numbers */
-		if (net_debug > 2)
-			printk("%s: WIC_PK_SEQ\n", dev->name);
-		if (wic_receive(nibble_timeout, status_addr,
-				 &rcv->nibble, &junk)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}
-		if (wic_receive(nibble_timeout, status_addr,
-				 &rcv->nibble, &junk)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}
-
-	case WIC_PK_DATA:
-		if (net_debug > 2)
-			printk("%s: WIC_PK_DATA: length %i\n", dev->name, 
-				rcv->length.h);
-		lbuf = rcv->skb->data;
-		do {
-			if (wic_receive(nibble_timeout, status_addr, 
-					 &rcv->nibble, &lbuf[rcv->byte])) {
-				restore_flags(flags);
-				return TIMEOUT;
-			}
-		} while (++rcv->byte < (rcv->length.h - 4));
-
-		/* receive pad byte */
-		if (rcv->length.h & 0x01)
-			wic_receive(nibble_timeout, status_addr, 
-					 &rcv->nibble, &lbuf[rcv->byte]);
-		
-		do {
-			rcv->checksum += lbuf[--rcv->byte];
-		} while (rcv->byte);
-
-		rcv->state = WIC_PK_CHECKSUM;
-
-	case WIC_PK_CHECKSUM:
-		if (net_debug > 2)
-			printk("%s: WIC_PK_CHECKSUM\n", dev->name);
-		if (wic_receive(nibble_timeout, status_addr,
-				 &rcv->nibble, &junk)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}
-		outb(0, PAR_DATA(dev));
-		rcv->state = WIC_PK_DONE;
-
-	case WIC_PK_DONE:
-		if (net_debug > 2)
-			printk("%s: WIC_PK_DONE\n", dev->name);
-		/* Inform the upper layer for the arrival of a packet. */
-		netif_rx(rcv->skb);
-		nl->enet_stats.rx_packets++;
-		rcv->skb = NULL;
-		if (net_debug > 2)
-			printk("%s: receive end\n", dev->name);
-
-		/* Close the connection. */
-		if (snd->state != WIC_PK_DONE) {
-			nl->connection = WIC_CN_SEND;
-			restore_flags(flags);
-			queue_task(&nl->immediate, &tq_immediate);
-			save |= 0x10; /* enable */
-			outb(save, PAR_CONTROL(dev));
-			enable_irq(dev->irq);
-			return OK;
-		} else {
-			nl->connection = WIC_CN_NONE;
-			restore_flags(flags);
-			save |= 0x10; /* enable */
-			outb(save, PAR_CONTROL(dev));
-			enable_irq(dev->irq);
-			return OK;
-		}
-	}
-	restore_flags(flags);
-	return OK;
-}
-
-/* WIC_SEND --- send a byte (two nibbles) 
-   Returns OK on success, TIMEOUT when timeout    */
-inline int
-wic_send(unsigned short nibble_timeout, unsigned short data_addr,
-	  enum wic_nibble_state *ns_p, unsigned char data)
-{
-unsigned int cx;
-
-	cx = LOOPCNT;
-	while ((inb(data_addr+1) & 0x80) == ((tog<<7) & 0x80)) {
-		if (--cx == 0) {
-			return -TIMEOUT;
-		}
-	}
-	outb(data, data_addr);
-	outb(tog | save, data_addr+2);
-	tog ^= 0x01;
-	return OK;
-}
-
-/* WIC_SEND_PACKET --- send a packet */
-int
-wic_send_packet(struct device *dev, struct net_local *nl,
-		 struct wic_local *snd, struct wic_local *rcv)
-{
-	unsigned short data_addr = PAR_DATA(dev);
-	unsigned short nibble_timeout = nl->nibble;
-	unsigned char *lbuf;
-	unsigned int cx;
-	unsigned int pad = 2;
-	unsigned long flags;
-
-	if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
-		printk("%s: send skb lost\n", dev->name);
-		snd->state = WIC_PK_DONE;
-		snd->skb = NULL;
-		save |= 0x10; /* enable */
-		outb(save, PAR_CONTROL(dev));
-		enable_irq(dev->irq);
-		return ERROR;
-	}
-
-	save_flags(flags);	
-	cli();
-	switch (snd->state) {
-	case WIC_PK_TRIGGER:
-	
-		if (nl->connection == WIC_CN_RECEIVE) {
-			/* interrupted */
-			nl->enet_stats.collisions++;
-			restore_flags(flags);
-			if (net_debug > 1)
-				printk("%s: collision.\n", dev->name);
-			save |= 0x10; /* enable */
-			outb(save, PAR_CONTROL(dev));
-			enable_irq(dev->irq);
-			return OK;
-		}
-		
-		disable_irq(dev->irq);
-		save &= 0xef; /* disable */
-		outb(save, PAR_CONTROL(dev));
-		
-		/* interrupt controller */
-		tog = 3;
-		outb(0x06 | save, PAR_CONTROL(dev));
-			
-		cx = LOOPCNT;
-		while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) {
-			if (--cx == 0) {
-				restore_flags(flags);
-				return TIMEOUT;
-			}
-			if (cx == 10)
-				outb(0x02, PAR_CONTROL(dev));
-		}
-		
-		if (net_debug > 2)
-			printk("%s: send start\n", dev->name);
-		snd->state = WIC_PK_LENGTH_LSB;
-		snd->nibble = WIC_NB_BEGIN;
-		nl->timeout_count = 0;
-
-	case WIC_PK_LENGTH_LSB:
-		if (snd->length.h & 0x01)
-			pad = 3;
-		else
-			pad = 2;
-		snd->length.h += (4 + pad); /* len + seq + data + pad */
-		if (net_debug > 2)
-			printk("%s: WIC_PK_LENGTH_LSB: length = %i\n", 
-				dev->name, snd->length.h);
-
-		if (wic_send(nibble_timeout, data_addr,
-			      &snd->nibble, snd->length.b.lsb)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}
-		snd->state = WIC_PK_LENGTH_MSB;
-
-	case WIC_PK_LENGTH_MSB:
-		if (net_debug > 2)
-			printk("%s: WIC_PK_LENGTH_MSB\n", dev->name);
-		if (wic_send(nibble_timeout, data_addr,
-			      &snd->nibble, snd->length.b.msb)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}
-		snd->state = WIC_PK_DATA;
-		snd->byte = 0;
-		snd->checksum = 0;
-
-	case WIC_PK_DATA:
-		/* adjust length back to data only */
-		snd->length.h -= (4 + pad); /* len + seq + data + pad */
-		/* send 2 byte sequence number */
-		if (net_debug > 2)
-			printk("%s: WIC_SEQ\n", dev->name);
-		if (wic_send(nibble_timeout, data_addr,
-			      &snd->nibble, 0)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}
-		if (wic_send(nibble_timeout, data_addr,
-			      &snd->nibble, 0)) {
-			restore_flags(flags);
-			return TIMEOUT;
-		}	
-		if (net_debug > 2)
-			printk("%s: WIC_PK_DATA\n", dev->name);
-
-		do {
-			if (wic_send(nibble_timeout, data_addr,
-				      &snd->nibble, lbuf[snd->byte])) {
-				restore_flags(flags);
-				return TIMEOUT;
-			}
-		}
-		while (++snd->byte < snd->length.h);
-		
-		do
-			snd->checksum += lbuf[--snd->byte];
-		while (snd->byte);
-
-		snd->state = WIC_PK_CHECKSUM;
-
-	case WIC_PK_CHECKSUM:
-		/* send pad bytes */
-		if (net_debug > 2)
-			printk("%s: WIC_PK_PAD: %i bytes\n", 
-				dev->name, pad);
-		while(pad--)
-			if (wic_send(nibble_timeout, data_addr,
-			      	&snd->nibble, 0)) {
-				restore_flags(flags);
-				return TIMEOUT;
-			}
-		dev_kfree_skb(snd->skb, FREE_WRITE);
-		nl->enet_stats.tx_packets++;
-		snd->state = WIC_PK_DONE;
-
-	case WIC_PK_DONE:
-		if (net_debug > 2)
-			printk("%s: WIC_PK_DONE\n", dev->name);
-		/* Close the connection */
-		outb (0x00, PAR_DATA(dev));
-		outb(save, PAR_CONTROL(dev));
-		
-		snd->skb = NULL;
-		if (net_debug > 2)
-			printk("%s: send end\n", dev->name);
-		nl->connection = WIC_CN_CLOSING;
-		nl->is_deferred = 1;
-		restore_flags(flags);
-		queue_task(&nl->deferred, &tq_timer);
-		save |= 0x10; /* enable */
-		outb(save, PAR_CONTROL(dev));
-		enable_irq(dev->irq);
-		return OK;
-	}
-	restore_flags(flags);
-	return OK;
-}
-
-int
-wic_connection_close(struct device *dev, struct net_local *nl,
-		      struct wic_local *snd, struct wic_local *rcv)
-{
-unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	if (nl->connection == WIC_CN_CLOSING) {
-		nl->connection = WIC_CN_NONE;
-		dev->tbusy = 0;
-		mark_bh(NET_BH);
-	}
-	restore_flags(flags);
-	return OK;
-}
-
-/* WIC_ERROR --- wait till other end settled */
-int
-wic_error(struct device *dev, struct net_local *nl,
-	   struct wic_local *snd, struct wic_local *rcv)
-{
-	unsigned char status;
-
-	status = inb(PAR_STATUS(dev));
-	if ((status & 0xf8) == 0x80) {
-		if (net_debug > 2)
-			printk("%s: reset interface.\n", dev->name);
-		nl->connection = WIC_CN_NONE;
-		dev->tbusy = 0;
-		dev->interrupt = 0;
-		save |= 0x10; /* enable */
-		outb(save, PAR_CONTROL(dev));
-		enable_irq(dev->irq);
-		mark_bh(NET_BH);
-	} else {
-		nl->is_deferred = 1;
-		queue_task(&nl->deferred, &tq_timer);
-	}
-
-	return OK;
-}
-
-/* Handle the parallel port interrupts. */
-void
-wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs)
-{
-	struct device *dev = (struct device *) irq2dev_map[irq];
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct wic_local *rcv = &nl->rcv_data;
-	unsigned long flags;
-
-	if (dev == NULL) {
-		printk ("wic_interrupt: irq %d for unknown device.\n", irq);
-		return;
-	}
-
-	if (dev->interrupt) {
-		return;
-	}
-
-	if (check_bfr(dev) < 0) {
-		return;
-	}
-	
-	dev->interrupt = 1;
-	if (net_debug > 3)
-		printk("%s: interrupt.\n", dev->name);
-
-	save_flags(flags);
-	cli();
-	switch (nl->connection) {
-	case WIC_CN_CLOSING:
-		dev->tbusy = 0;
-	case WIC_CN_NONE:
-	case WIC_CN_SEND:
-		dev->last_rx = jiffies;
-		rcv->state = WIC_PK_TRIGGER;
-		nl->connection = WIC_CN_RECEIVE;
-		nl->timeout_count = 0;
-		restore_flags(flags);
-		queue_task(&nl->immediate, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
-		break;
-
-	case WIC_CN_RECEIVE:
-		printk("%s: receive interrupt when receiving packet\n", dev->name);
-		restore_flags(flags);
-		break;
-
-	case WIC_CN_ERROR:
-		printk("%s: receive interrupt in error state\n", dev->name);
-		restore_flags(flags);
-		break;
-	}
-}
-
-int
-wic_rebuild_header(void *buff, struct device *dev, unsigned long dst,
-		    struct sk_buff *skb)
-{
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct ethhdr *eth = (struct ethhdr *)buff;
-	int i;
-
-	if ((dev->flags & IFF_NOARP)==0)
-		return nl->orig_rebuild_header(buff, dev, dst, skb);
-
-	if (eth->h_proto != htons(ETH_P_IP)) {
-		printk("wic_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
-		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-		return 0;
-	}
-
-	for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
-		eth->h_dest[i] = 0xfc;
-	memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
-	return 0;
-}
-
-int
-wic_tx_packet(struct sk_buff *skb, struct device *dev)
-{
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct wic_local *snd = &nl->snd_data;
-	unsigned long flags;
-
-	if (dev->tbusy)
-		return 1;
-
-	/* 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;
-	}
-
-	if (set_bit(0, (void*)&dev->tbusy) != 0) {
-		printk("%s: Transmitter access conflict.\n", dev->name);
-		return 1;
-	}
-
-	if (skb->len > dev->mtu) {
-		printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
-		dev->tbusy = 0;
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-
-	if (net_debug > 2)
-		printk("%s: send request\n", dev->name);
-
-	save_flags(flags);
-	cli();
-	dev->trans_start = jiffies;
-	snd->skb = skb;
-	snd->length.h = skb->len;
-	snd->state = WIC_PK_TRIGGER;
-	if (nl->connection == WIC_CN_NONE) {
-		nl->connection = WIC_CN_SEND;
-		nl->timeout_count = 0;
-	}
-	restore_flags(flags);
-	queue_task(&nl->immediate, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-
-	return 0;
-}
-
-/* Open/initialize the board.  This is called (in the current kernel)
-   sometime after booting when the 'ifconfig' program is run.
-
-   This routine gets exclusive access to the parallel port by allocating
-   its IRQ line.
- */
-int
-wic_open(struct device *dev)
-{
-	struct net_local *nl = (struct net_local *)dev->priv;
-	unsigned long flags;
-
-	if (dev->irq == 0) {
-		printk("%s: IRQ is not set.  Please set it by ifconfig.\n", dev->name);
-		return -EAGAIN;
-	}
-	save_flags(flags);
-	cli();
-	check_bfr(dev);
-	if (request_irq(dev->irq , wic_interrupt, 0, dev->name, NULL) != 0) {
-		sti();
-		printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
-		return -EAGAIN;
-	}
-	irq2dev_map[dev->irq] = dev;
-	restore_flags(flags);
-
-	save |= 0x10; /* enable */
-	outb(save, PAR_CONTROL(dev));
-	/* Initialize the state machine. */
-	nl->rcv_data.state = nl->snd_data.state = WIC_PK_DONE;
-	nl->rcv_data.skb = nl->snd_data.skb = NULL;
-	nl->connection = WIC_CN_NONE;
-	nl->is_deferred = 0;
-
-	dev->interrupt = 0;
-	dev->start = 1;
-	dev->tbusy = 0;
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/* The inverse routine to wic_open (). */
-int
-wic_close(struct device *dev)
-{
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct wic_local *snd = &nl->snd_data;
-	struct wic_local *rcv = &nl->rcv_data;
-
-	dev->tbusy = 1;
-	dev->start = 0;
-	cli();
-	free_irq(dev->irq, NULL);
-	irq2dev_map[dev->irq] = NULL;
-	nl->is_deferred = 0;
-	nl->connection = WIC_CN_NONE;
-	sti();
-	outb(0x00, PAR_DATA(dev));
-
-	snd->state = WIC_PK_DONE;
-	if (snd->skb) {
-		snd->skb->free = 1;
-		dev_kfree_skb(snd->skb, FREE_WRITE);
-		snd->skb = NULL;
-	}
-	rcv->state = WIC_PK_DONE;
-	if (rcv->skb) {
-		rcv->skb->free = 1;
-		kfree_skb(rcv->skb, FREE_READ);
-		rcv->skb = NULL;
-	}
-
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-struct enet_statistics *
-wic_get_stats(struct device *dev)
-{
-	struct net_local *nl = (struct net_local *)dev->priv;
-	struct enet_statistics *r = &nl->enet_stats;
-
-	return r;
-}
-
-int
-wic_config(struct device *dev, struct ifmap *map)
-{
-	if (dev->flags & IFF_UP)
-		return -EBUSY;
-
-	if (map->base_addr != (unsigned long)-1
-	    && map->base_addr != dev->base_addr)
-		printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
-
-	if (map->irq != (unsigned char)-1)
-		dev->irq = map->irq;
-	return 0;
-}
-
-int
-wic_ioctl(struct device *dev, struct ifreq *rq, int cmd)
-{
-	struct wicconf wc;
-	int err;
-	char len = 0;
-	unsigned long flags;
-
-	err=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(struct wicconf));
-	if (err)
-		return err;
-	memcpy_fromfs(&wc, rq->ifr_data, sizeof(struct wicconf));
-	switch(wc.pcmd) {
-		case WIC_AYT:
-			strcpy(wc.data, version);
-			wc.len = strlen(wc.data);
-			memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf));
-			/* return 0; */
-			break;
-		case WIC_RESET:
-			wic_reset(dev);
-			return(0);
-			/* break; */
-		case WIC_SETSN:
-			len = 17;
-			break;
-		case WIC_SETPS:
-			len = 3;
-			break;
-		case WIC_SETAF:
-		case WIC_SETGPF:
-			len = 2;
-			break;
-		case WIC_SETNET:
-			len = 23;
-			break;
-		case WIC_SETSYS:
-			len = 15;
-			break;
-		case WIC_GETVERH:
-		case WIC_GETNL:
-		case WIC_GETSN:
-		case WIC_CLRSTATS:
-		case WIC_GETSTATS:
-		case WIC_GETVERM:
-		case WIC_GETNET:
-		case WIC_GETSYS:
-			len = 1;
-			break;	
-		default:
-			return -EOPNOTSUPP;
-	}
-
-	/* Wait for lock to free */
-      	while (set_bit(0, (void *)&dev->tbusy) != 0); 
-	save_flags(flags);
-	cli();
-
-	disable_irq(dev->irq);
-	save &= 0xef; /* disable */
-	outb(save, PAR_CONTROL(dev));
-	err = check_bfr(dev);
-	tog = 3;
-	err = send_cmd(dev, (unsigned char *)&wc, len);
-
-	if (wc.pcmd & 0x40) {	/* response */
-		len = (char)recv_cmd_resp(dev, wc.data);
-		while ((len == 1) && (wc.data[0] == 0x7)) { /* controller int */
-			len = (char)recv_cmd_resp(dev, wc.data);
-		}
-		save |= 0x10; /* enable */
-		outb(save, PAR_CONTROL(dev));
-		enable_irq(dev->irq);
-		wc.len = (len <0) ? 0 : len;
-		memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf));
-	} else {
-		save |= 0x10; /* enable */
-		outb(save, PAR_CONTROL(dev));
-		enable_irq(dev->irq);
-	}
-	restore_flags(flags);
-
-	outb(0, PAR_DATA(dev));
-	dev->tbusy = 0;
-	return 0;
-}
-
-int
-get_byte(struct device *dev, unsigned char *c)
-{
-unsigned int cx;
-
-	cx = LOOPCNT;
-	while ((inb(PAR_STATUS(dev)) & 0x08) != ((tog << 3)&0x08)) {
-		if (--cx == 0) {
-			return(-TIMEOUT);
-		}
-	}
-	/* receive a byte of data */
-	*c = inb(PAR_DATA(dev));
-	tog ^= 0x01;
-	/* ack reception of data */
-	outb(tog| save, PAR_CONTROL(dev));
-	return OK;
-}
-
-int
-ack_resp(struct device *dev)
-{
-unsigned int cx;
-	
-	outb(save | 0x27, PAR_CONTROL(dev));
-
-	/* wait for controller to remove interrupt [Ack(low), Busy(low)] */
-	cx = LOOPCNT;
-	while ((inb(PAR_STATUS(dev)) & 0xc0) != 0x80) {
-		if (--cx == 0) {
-			return -TIMEOUT;
-		}
-	}
-	
-	outb(save | 0x22, PAR_CONTROL(dev));
-	cx = LOOPCNT;
-	while ((inb(PAR_STATUS(dev)) & 0x08) == 0x08) {
-		if (--cx == 0) {
-			return TIMEOUT;
-		}
-	}
-	tog |= 0x20;
-	tog &= 0xfe;
-	return OK;
-}
-
-void
-wic_reset(struct device *dev)
-{
-unsigned char stat;
-
-	stat = inb(PAR_CONTROL(dev));
-	outb(0, PAR_DATA(dev));
-	outb(stat | 0x08, PAR_CONTROL(dev));
-	outb(stat & 0xf7, PAR_CONTROL(dev));
-	dev->tbusy = 0;
-	dev->interrupt = 0;
-	tog = 3;
-	save = 0;
-	return;
-}
-
-int
-check_bfr(struct device *dev)
-{
-unsigned char c0, l;
-
-	if ((inb(PAR_STATUS(dev)) & 0xc8) == 0x48) {
-		save |= 0x80;
-		outb(0x23| save, PAR_CONTROL(dev));
-		ack_resp(dev);
-		get_byte(dev, &l);	/* len */
-		while (l--) {
-			get_byte(dev, &c0);
-		}
-		get_byte(dev, &c0);
-		save &=0x7f;
-		outb(0, PAR_DATA(dev));
-		return -l;
-	} else
-	return (0);
-}
-
-
-int
-recv_cmd_resp(struct device *dev, unsigned char *buf)
-{
-unsigned char cksum = 0;
-int err;
-unsigned char c0 = 0;
-int len;
-int savelen;
-unsigned int cx;
-int i;
-
-	tog &= 0xfe;
-	cx = LOOPCNT;
-	while ((inb(PAR_STATUS(dev)) & 0xc8) != 0x48) {
-		if (--cx == 0) {
-			/* clear Busy */
-			outb(0, PAR_DATA(dev));
-			printk("rcv_cmd_resp: timeout\n");
-			return -TIMEOUT;
-		}
-	}
-	
-	/* acknowledge the interrupt */
-	i = ack_resp(dev);
-
-	/* get length */
-	err = get_byte(dev, &c0);
-	if (err < 0) {
-		printk("get_byte1: failed\n");
-		return(err);
-	}
-	len = c0;
-	savelen = len;
-
-	/* get data */
-	while(len--) {
-		err = get_byte(dev, &c0);
-		if (err < 0) {
-			printk("get_byte2: failed\n");
-			return(err);
-		}
-		outb(0, PAR_DATA(dev));	
-		*buf = c0;
-		cksum += c0;
-		buf++;
-	}	
-	/* get cksum */
-	err = get_byte(dev, &c0);
-	if (err < 0) {
-		printk("get_byte3: failed\n");
-		return(err);
-	}
-	if (cksum != c0) {
-		printk("cksum failed\n");
-		return(-3);
-	}
-	/* get trailing byte, if any... */
-	get_byte(dev, &c0);
-	return(savelen);
-}	
-
-int
-send_byte(struct device *dev, unsigned char c)
-{
-unsigned int cx;
-
-	cx = LOOPCNT;
-	while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog<<7) & 0x80)) {
-		if (--cx == 0) {
-			return(-TIMEOUT);
-		}
-	}
-	outb(c, PAR_DATA(dev));
-	outb(save |tog, PAR_CONTROL(dev));
-	tog ^= 0x01;
-	return OK;
-}
-
-
-int
-send_cmd(struct device *dev, unsigned char *cmd, char len)
-{
-unsigned char cksum = 0;
-int err = 0;
-unsigned int cx;
-
-	/* interrupt controller */
-	outb(save | 0x04, PAR_CONTROL(dev));
-	/* wait for ACK */
-	cx = LOOPCNT;
-	while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) {
-		if (--cx == 0) 
-			return -TIMEOUT;
-		if (cx == 10)
-			outb(0x02, PAR_CONTROL(dev));
-	}
-	/* cmd coming... */
-	outb(save | 0x02, PAR_CONTROL(dev));
-	/* send length byte */
-	err = send_byte(dev, (unsigned char)len);
-	
-	/* send data */
-	while (len--) {
-		err = send_byte(dev, *cmd);	
-		if (err < 0) {
-			return err;
-		}
-		cksum += *cmd;
-		cmd++;
-	}
-	
-	/* send cksum byte */
-	err = send_byte(dev, cksum);	
-	if (err < 0)
-		return err;
-
-	cx = LOOPCNT;
-	while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog <<7)&0x80)) {
-		if (--cx == 0) 
-			return -TIMEOUT;
-	}
-	save |= 0x80;
-	outb(save | 0x23, PAR_CONTROL(dev));
-	outb(0, PAR_DATA(dev));
-	return OK;	
-}
-
-#ifdef MODULE
-struct device dev_wic0 = 
-{
-	"wic0" /*"wic"*/,
-	0, 0, 0, 0,		/* memory */
-	0x3BC, 5,		/* base, irq */
-	0, 0, 0, NULL, wic_init 
-};
-
-struct device dev_wic1 = 
-{
-	"wic1" /*"wic"*/,
-	0, 0, 0, 0,		/* memory */
-	0x378, 7,		/* base, irq */
-	0, 0, 0, NULL, wic_init 
-};
-
-struct device dev_wic2 = 
-{
-	"wic2" /*"wic"*/,
-	0, 0, 0, 0,		/* memory */
-	0x278, 2,		/* base, irq */
-	0, 0, 0, NULL, wic_init 
-};
-
-int
-init_module(void)
-{
-	int devices=0;
-
-	if (register_netdev(&dev_wic0) != 0)
-		devices++;
-	if (register_netdev(&dev_wic1) != 0)
-		devices++;
-	if (register_netdev(&dev_wic2) != 0)
-		devices++;
-	if (devices == 0)
-		return -EIO;
-	return 0;
-}
-
-void
-cleanup_module(void)
-{
-	if (dev_wic0.priv) {
-		unregister_netdev(&dev_wic0);
-		release_region(PAR_DATA(&dev_wic0), 3);
-		kfree_s(dev_wic0.priv, sizeof(struct net_local));
-		dev_wic0.priv = NULL;
-	}
-	if (dev_wic1.priv) {
-		unregister_netdev(&dev_wic1);
-		release_region(PAR_DATA(&dev_wic1), 3);
-		kfree_s(dev_wic1.priv, sizeof(struct net_local));
-		dev_wic1.priv = NULL;
-	}
-	if (dev_wic2.priv) {
-		unregister_netdev(&dev_wic2);
-		release_region(PAR_DATA(&dev_wic2), 3);
-		kfree_s(dev_wic2.priv, sizeof(struct net_local));
-		dev_wic2.priv = NULL;
-	}
-}
-#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c wic.c"
- * End:
- */
diff -u --recursive --new-file v2.0.35/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.0.35/linux/drivers/pci/pci.c	Mon Jul 13 13:46:30 1998
+++ linux/drivers/pci/pci.c	Sun Nov 15 10:33:04 1998
@@ -262,6 +262,8 @@
 	DEVICE( WINBOND,	WINBOND_82C105,	"SL82C105"),
 	DEVICE( WINBOND,	WINBOND_83C553,	"W83C553"),
 	DEVICE( DATABOOK,      	DATABOOK_87144,	"DB87144"),
+	DEVICE( PLX,        PLX_SPCOM200, "SPCom 200 PCI serial I/O"),
+	DEVICE( PLX,        PLX_9050,   "PLX9050 PCI <-> IOBus Bridge"),
 	DEVICE( PLX,		PLX_9080,	"PCI9080 I2O"),
 	DEVICE( MADGE,		MADGE_MK2,	"Smart 16/4 BM Mk2 Ringnode"),
 	DEVICE( 3COM,		3COM_3C339,	"3C339 TokenRing"),
@@ -400,6 +402,7 @@
 	DEVICE( LITEON,		LITEON_LNE100TX,"LNE100TX"),
 	DEVICE( NP,		NP_PCI_FDDI,	"NP-PCI"),       
 	DEVICE( ATT,		ATT_L56XMF,	"L56xMF"),
+	DEVICE( SPECIALIX,	SPECIALIX_IO8,	"IO8+/PCI"),
 	DEVICE( SPECIALIX,	SPECIALIX_XIO,	"XIO/SIO host"),
 	DEVICE( SPECIALIX,	SPECIALIX_RIO,	"RIO host"),
 	DEVICE( AURAVISION,	AURAVISION_VXP524,"VXP524"),
@@ -431,6 +434,7 @@
 	DEVICE( OPTIBASE,	OPTIBASE_VPLEX,	"VideoPlex"),
 	DEVICE( OPTIBASE,	OPTIBASE_VPLEXCC,"VideoPlex CC"),
 	DEVICE( OPTIBASE,	OPTIBASE_VQUEST,"VideoQuest"),
+	DEVICE( ASIX,		ASIX_88140, "88140"),
 	DEVICE( SATSAGEM,	SATSAGEM_PCR2101,"PCR2101 DVB receiver"),
 	DEVICE( SATSAGEM,	SATSAGEM_TELSATTURBO,"Telsat Turbo DVB"),
 	DEVICE( ENSONIQ,	ENSONIQ_AUDIOPCI,"AudioPCI"),
@@ -501,6 +505,9 @@
 	DEVICE( INTEL,		INTEL_82443BX_0,"440BX - 82443BX Host"),
 	DEVICE( INTEL,		INTEL_82443BX_1,"440BX - 82443BX AGP"),
 	DEVICE( INTEL,		INTEL_82443BX_2,"440BX - 82443BX Host (no AGP)"),
+	DEVICE( INTEL,		INTEL_82443GX_0,"440GX - 82443GX Host"),
+	DEVICE( INTEL,		INTEL_82443GX_1,"440GX - 82443GX AGP"),
+	DEVICE( INTEL,		INTEL_82443GX_2,"440GX - 82443GX Host (no AGP)"),
 	DEVICE( INTEL,		INTEL_P6,	"Orion P6"),
  	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion P6"),
 	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
@@ -522,6 +529,10 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7883,	"AIC-7883U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
 	DEVICE( ADAPTEC,	ADAPTEC_1030,	"ABA-1030 DVB receiver"),
+  DEVICE( ADAPTEC2, ADAPTEC2_2940U2, "AHA-2940U2"),
+  DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"),
+  DEVICE( ADAPTEC2, ADAPTEC2_3940U2, "AHA-3940U2"),
+  DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
 	DEVICE( TIGERJET,	TIGERJET_300,	"Tiger300 ISDN"),
 	DEVICE( ARK,		ARK_STING,	"Stingray"),
@@ -812,6 +823,8 @@
 	      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_NVIDIA_SGS:    return "NVidia/SGS Thomson";
+ 	      case PCI_VENDOR_ID_ENSONIQ:	return "Ensoniq";	
 	      case PCI_VENDOR_ID_SYMPHONY:	return "Symphony";
 	      case PCI_VENDOR_ID_TEKRAM:	return "Tekram";
 	      case PCI_VENDOR_ID_3DLABS:	return "3Dlabs";
@@ -821,8 +834,11 @@
 	      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_ADAPTEC2:	return "Adaptec";
 	      case PCI_VENDOR_ID_ATRONICS:	return "Atronics";
 	      case PCI_VENDOR_ID_ARK:		return "ARK Logic";
+	      case PCI_VENDOR_ID_ASIX:		return "ASIX";
+	      case PCI_VENDOR_ID_LITEON:	return "Lite-on";
 	      default:				return "Unknown vendor";
 	}
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v2.0.35/linux/drivers/scsi/BusLogic.c	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/BusLogic.c	Sun Nov 15 10:33:05 1998
@@ -26,8 +26,8 @@
 */
 
 
-#define BusLogic_DriverVersion		"2.0.14"
-#define BusLogic_DriverDate		"29 April 1998"
+#define BusLogic_DriverVersion		"2.0.15"
+#define BusLogic_DriverDate		"17 August 1998"
 
 
 #include <linux/version.h>
@@ -1190,7 +1190,8 @@
 		    BusLogic_ProbeInfoCount - FlashPointCount;
 		  memcpy(SavedProbeInfo,
 			 BusLogic_ProbeInfoList,
-			 sizeof(BusLogic_ProbeInfoList));
+			 BusLogic_ProbeInfoCount
+			 * sizeof(BusLogic_ProbeInfo_T));
 		  memcpy(&BusLogic_ProbeInfoList[0],
 			 &SavedProbeInfo[FlashPointCount],
 			 MultiMasterCount * sizeof(BusLogic_ProbeInfo_T));
@@ -3149,25 +3150,25 @@
 		  HostAdapter->TargetStatistics[CCB->TargetID]
 			       .CommandsCompleted++;
 		  if (BusLogic_GlobalOptions.TraceErrors)
-		      {
-			int i;
-			BusLogic_Notice("CCB #%ld Target %d: Result %X Host "
-					"Adapter Status %02X "
-					"Target Status %02X\n",
-					HostAdapter, CCB->SerialNumber,
-					CCB->TargetID, Command->result,
-					CCB->HostAdapterStatus,
-					CCB->TargetDeviceStatus);
-			BusLogic_Notice("CDB   ", HostAdapter);
-			for (i = 0; i < CCB->CDB_Length; i++)
-			  BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]);
-			BusLogic_Notice("\n", HostAdapter);
-			BusLogic_Notice("Sense ", HostAdapter);
-			for (i = 0; i < CCB->SenseDataLength; i++)
-			  BusLogic_Notice(" %02X", HostAdapter,
-					  Command->sense_buffer[i]);
-			BusLogic_Notice("\n", HostAdapter);
-		      }
+		    {
+		      int i;
+		      BusLogic_Notice("CCB #%ld Target %d: Result %X Host "
+				      "Adapter Status %02X "
+				      "Target Status %02X\n",
+				      HostAdapter, CCB->SerialNumber,
+				      CCB->TargetID, Command->result,
+				      CCB->HostAdapterStatus,
+				      CCB->TargetDeviceStatus);
+		      BusLogic_Notice("CDB   ", HostAdapter);
+		      for (i = 0; i < CCB->CDB_Length; i++)
+			BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]);
+		      BusLogic_Notice("\n", HostAdapter);
+		      BusLogic_Notice("Sense ", HostAdapter);
+		      for (i = 0; i < CCB->SenseDataLength; i++)
+			BusLogic_Notice(" %02X", HostAdapter,
+					Command->sense_buffer[i]);
+		      BusLogic_Notice("\n", HostAdapter);
+		    }
 		}
 	      break;
 	    }
@@ -4219,7 +4220,6 @@
   BusLogic_TargetStatistics_T *TargetStatistics;
   int TargetID, Length;
   char *Buffer;
-  if (WriteFlag) return 0;
   for (HostAdapter = BusLogic_FirstRegisteredHostAdapter;
        HostAdapter != NULL;
        HostAdapter = HostAdapter->Next)
@@ -4231,6 +4231,14 @@
       return 0;
     }
   TargetStatistics = HostAdapter->TargetStatistics;
+  if (WriteFlag)
+    {
+      HostAdapter->ExternalHostAdapterResets = 0;
+      HostAdapter->HostAdapterInternalErrors = 0;
+      memset(TargetStatistics, 0,
+	     BusLogic_MaxTargetDevices * sizeof(BusLogic_TargetStatistics_T));
+      return 0;
+    }
   Buffer = HostAdapter->MessageBuffer;
   Length = HostAdapter->MessageBufferLength;
   Length += sprintf(&Buffer[Length], "\n\
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.0.35/linux/drivers/scsi/Config.in	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/Config.in	Sun Nov 15 10:33:05 1998
@@ -30,6 +30,7 @@
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
+dep_tristate 'AMI MegaRAID support' CONFIG_SCSI_MEGARAID $CONFIG_SCSI
 dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
 if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
     bool '  Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c
--- v2.0.35/linux/drivers/scsi/FlashPoint.c	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/FlashPoint.c	Sun Nov 15 10:33:05 1998
@@ -3165,11 +3165,11 @@
 
 	if(RD_HARPOON(ioport + hp_page_ctrl) & BIOS_SHADOW)
 	{
-		pAdapterInfo->ai_FlashRomSize = 64 * 1024;	/* 64k Rom */
+		pAdapterInfo->ai_FlashRomSize = 64 * 1024;	/* 64k ROM */
 	}
 	else
 	{
-		pAdapterInfo->ai_FlashRomSize = 32 * 1024;	/* 32k Rom */
+		pAdapterInfo->ai_FlashRomSize = 32 * 1024;	/* 32k ROM */
 	}
 
    pAdapterInfo->ai_stateinfo |= (FAST20_ENA | TAG_QUEUE_ENA);
@@ -4944,7 +4944,7 @@
  *
  * Function: Sccb_bad_isr
  *
- * Description: Some type of interrupt has occured which is slightly
+ * Description: Some type of interrupt has occurred which is slightly
  *              out of the ordinary.  We will now decode it fully, in
  *              this routine.  This is broken up in an attempt to save
  *              processing time.
@@ -7038,7 +7038,7 @@
  *
  * Function: sssyncv
  *
- * Description: Write the desired value to the Sync Regisiter for the
+ * Description: Write the desired value to the Sync Register for the
  *              ID specified.
  *
  *---------------------------------------------------------------------*/
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.0.35/linux/drivers/scsi/Makefile	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/Makefile	Sun Nov 15 10:33:05 1998
@@ -363,6 +363,13 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_MEGARAID),y)
+L_OBJS += megaraid.o
+else
+  ifeq ($(CONFIG_SCSI_MEGARAID),m)
+  M_OBJS += megaraid.o
+  endif
+endif
 
 ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
 L_OBJS += ide-scsi.o
@@ -376,7 +383,7 @@
 gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h
 	$(CC) $(CFLAGS) $(GDTH) -c gdth.c 
 
-aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
+aic7xxx.o: aic7xxx.c aic7xxx_seq.c aic7xxx_reg.h
 	$(CC) $(CFLAGS) -c -o $@ aic7xxx.c
 
 seagate.o: seagate.c
@@ -394,6 +401,9 @@
 
 g_NCR5380.o: g_NCR5380.c
 	$(CC) $(CFLAGS) -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" -c g_NCR5380.c
+
+megaraid.o: megaraid.c
+	$(CC) $(CFLAGS) -c megaraid.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.35/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v2.0.35/linux/drivers/scsi/README.BusLogic	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/README.BusLogic	Sun Nov 15 10:33:05 1998
@@ -1,10 +1,11 @@
 	   BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
 
-			 Version 2.0.14 for Linux 2.0
+			 Version 2.0.15 for Linux 2.0
+			 Version 2.1.15 for Linux 2.1
 
 			      PRODUCTION RELEASE
 
-				 29 April 1998
+				17 August 1998
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -80,7 +81,7 @@
 
 Mylex Corporation is located at 34551 Ardenwood Blvd., Fremont, California
 94555, USA and can be reached at 510/796-6100 or on the World Wide Web at
-http://www.mylex.com.  Mylex Technical Support can be reached by electronic
+http://www.mylex.com.  Mylex HBA Technical Support can be reached by electronic
 mail at techsup@mylex.com, by Voice at 510/608-2400, or by FAX at 510/745-7715.
 Contact information for offices in Europe and Japan is available on the Web
 site.
@@ -584,16 +585,16 @@
 
 			      DRIVER INSTALLATION
 
-This distribution was prepared for Linux kernel version 2.0.33, but should be
+This distribution was prepared for Linux kernel version 2.0.35, but should be
 compatible with 2.0.4 or any later 2.0 series kernel.
 
 To install the new BusLogic SCSI driver, you may use the following commands,
 replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-2.0.14.tar.gz
+  tar -xvzf BusLogic-2.0.15.tar.gz
   mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
-  patch -p < BusLogic.patch
+  patch -p0 < BusLogic.patch (only for 2.0.33 and below)
   cd linux
   make config
   make depend
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.Mylex linux/drivers/scsi/README.Mylex
--- v2.0.35/linux/drivers/scsi/README.Mylex	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/README.Mylex	Sun Nov 15 10:33:05 1998
@@ -1,6 +1,5 @@
 Please see the file README.BusLogic for information about Linux support for
 Mylex (formerly BusLogic) MultiMaster and FlashPoint SCSI Host Adapters.
 
-The Mylex DAC960 PCI RAID Controllers are not supported at the present time,
-but work on a Linux driver for the DAC960 is in progress.  Please consult
+The Mylex DAC960 PCI RAID Controllers are now supported.  Please consult
 http://www.dandelion.com/Linux/ for further information on the DAC960 driver.
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx
--- v2.0.35/linux/drivers/scsi/README.aic7xxx	Sun Nov 15 10:49:43 1998
+++ linux/drivers/scsi/README.aic7xxx	Sun Nov 15 10:33:06 1998
@@ -21,15 +21,24 @@
     AHA-2940W              
     AHA-2940U              
     AHA-2940UW
-    AHA-2940AU             
+    AHA-2940AU 
+    AHA-2940U2W
+    AHA-2940U2
+    AHA-2940U2B
+    AHA-2940U2BOEM
     AHA-2944D              
     AHA-2944WD
     AHA-2944UD
     AHA-2944UWD
+    AHA-2950U2
+    AHA-2950U2W
+    AHA-2950U2B
     AHA-3940
     AHA-3940U
     AHA-3940W
     AHA-3940UW
+    AHA-3940U2W
+    AHA-3950U2B
     AHA-3985
     AHA-3985U
     AHA-3985W
@@ -42,13 +51,14 @@
     AIC-786x
     AIC-787x
     AIC-788x
-    AIC-7895
+    AIC-789x
 
     Bus Types
     ----------------------------
     W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support
         SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s.
     U - Ultra SCSI, transfer rates up to 40MB/s.
+    U2- Ultra 2 SCSI, transfer rates up to 80MB/s.
     D - Differential SCSI.
     T - Twin Channel SCSI. Up to 14 SCSI devices.
 
@@ -72,7 +82,7 @@
       (Original Linux FTP/patch maintainer)
     Jess Johnson    jester@frenzy.com
       (AIC7xxx FAQ author)
-    Doug Ledford    dledford@dialnet.net
+    Doug Ledford    dledford@redhat.com
       (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer)
     
     Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
@@ -100,40 +110,45 @@
     
   Boot Command line options
   ------------------------------
-    "aic7xxx=no_reset" -  Eliminate the SCSI reset delay during startup.
-        Some SCSI devices need some extra time to reset.
-    "aic7xxx=reverse_scan" - Have the driver register the SCSI cards in the
-        reverse of the normal order.  This may help those people who have more
-        than one PCI Adaptec controller force the correct controller to be
-        scsi0 under linux so that their boot hard drive is also sda under
-        linux
+    "aic7xxx=no_reset" -  Eliminate the SCSI bus reset during startup.
+        Some SCSI devices need the initial reset that this option disables
+	in order to work.  If you have problems at bootup, please make sure
+	you aren't using this option.
+	
+    "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at
+        bootup by scanning from the highest numbered PCI device to the
+	lowest numbered PCI device, others do just the opposite and scan
+	from lowest to highest numbered PCI device.  There is no reliable
+	way to autodetect this ordering.  So, we default to the most common
+	order, which is lowest to highest.  Then, in case your motherboard
+	scans from highest to lowest, we have this option.  If your BIOS
+	finds the drives on controller A before controller B but the linux
+	kernel finds your drives on controller B before A, then you should
+	use this option.
+	
     "aic7xxx=extended" - Force the driver to detect extended drive translation
         on your controller.  This helps those people who have cards without
         a SEEPROM make sure that linux and all other operating systems think
         the same way about your hard drives.
+	
     "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel
         to use the correct IRQ type for your card.  This only applies to EISA
         based controllers.  On these controllers, 0 is for Edge triggered
         interrupts, and 1 is for Level triggered interrupts.  If you aren't
         sure or don't know which IRQ trigger type your EISA card uses, then
         let the kernel autodetect the trigger type.
+	
     "aic7xxx=verbose" - This option can be used in one of two ways.  If you
-        simply specify aic7xxx=verbose, then the kernel will automatically pick
-        the default set of verbose messages for you to see.  Alternatively, you
-        can specify the command as "aic7xxx=verbose:0xXXXX" where the X entries
-        are replaced with hexadecimal digits.  This option is a bit field type
-        option.  For a full listing of the available options, search for the
-        #define VERBOSE_xxxxxx lines in the aic7xxx.c file.  If you want verbose
-        messages, then it is recommended that you simply use the aic7xxx=verbose
-        variant of this command.
-    "aic7xxx=7895_irq_hack:x" - This option enables some work around code to
-        fix a bug in the Tyan Thunder II motherboard BIOS.  The BIOS
-        incorrectly sets the IRQs on the two channels of the 7895 to two
-        different values even though the motherboard hardware doesn't support
-        this mode of operation.  The valid values for x are: 0 to force
-        both channels to use the IRQ assigned to Channel A, 1 to force both
-        channels to use the IRQ assigned to Channel B, and -1 will disable
-        this horrible abomination of a hack.  The default is disabled (-1).
+        simply specify aic7xxx=verbose, then the kernel will automatically
+	pick the default set of verbose messages for you to see.
+	Alternatively, you can specify the command as 
+	"aic7xxx=verbose:0xXXXX" where the X entries are replaced with
+	hexadecimal digits.  This option is a bit field type option.  For
+	a full listing of the available options, search for the 
+	#define VERBOSE_xxxxxx lines in the aic7xxx.c file.  If you want
+	verbose messages, then it is recommended that you simply use the
+	aic7xxx=verbose variant of this command.
+	
     "aic7xxx=pci_parity:x" - This option controls whether or not the driver
         enables PCI parity error checking on the PCI bus.  By default, this
         checking is disabled.  To enable the checks, simply specify pci_parity
@@ -145,6 +160,145 @@
         NOTE: In order to get Even PCI parity checking, you must use the
         version of the option that does not include the : and a number at
         the end (unless you want to enter exactly 2^32 - 1 as the number).
+	
+    "aic7xxx=no_probe" - This option will disable the probing for any VLB
+        based 2842 controllers and any EISA based controllers.  This is
+	needed on certain newer motherboards where the normal EISA I/O ranges
+	have been claimed by other PCI devices.  Probing on those machines
+	will often result in the machine crashing or spontaneously rebooting
+	during startup.  Examples of machines that need this are the
+	Dell PowerEdge 6300 machines.
+	
+    "aic7xxx=panic_on_abort" - This option is for debugging and will cause
+        the driver to panic the linux kernel and freeze the system the first
+	time the drivers abort or reset routines are called.  This is most
+	helpful when some problem causes infinite reset loops that scroll too
+	fast to see.  By using this option, you can write down what the errors
+	actually are and send that information to me so it can be fixed.
+	
+    "aic7xxx=dump_card" - This option will print out the *entire* set of
+        configuration registers on the card during the init sequence.  This
+	is a debugging aid used to see exactly what state the card is in
+	when we finally finish our initialization routines.  If you don't
+	have documentation on the chipsets, this will do you absolutely
+	no good unless you are simply trying to write all the information
+	down in order to send it to me.
+	
+    "aic7xxx=dump_sequencer" - This is the same as the above options except
+        that instead of dumping the register contents on the card, this
+	option dumps the contents of the sequencer program RAM.  This gives
+	the ability to verify that the instructions downloaded to the
+	card's sequencer are indeed what they are suppossed to be.  Again,
+	unless you have documentation to tell you how to interpret these
+	numbers, then it is totally useless.
+	
+    "aic7xxx=override_term:0xffffffff" - This option is used to force the
+    	termination on your SCSI controllers to a particular setting.  This
+	is a bit mask variable that applies for up to 8 aic7xxx SCSI channels.
+	Each channel gets 4 bits, divided as follows:
+	bit   3   2   1   0
+	      |   |   |   Enable/Disable Single Ended Low Byte Termination
+	      |   |   En/Disable Single Ended High Byte Termination
+	      |   En/Disable Low Byte LVD Termination
+	      En/Disable High Byte LVD Termination
+
+	The upper 2 bits that deal with LVD termination only apply to Ultra2
+	controllers.  Futhermore, due to the current Ultra2 controller
+	designs, these bits are tied together such that setting either bit
+	enables both low and high byte LVD termination.  It is not possible
+	to only set high or low byte LVD termination in this manner.  This is
+	an artifact of the BIOS definition on Ultra2 controllers.  For other
+	controllers, the only important bits are the two lowest bits.  Setting
+	the higher bits on non-Ultra2 controllers has no effect.  A few
+	examples of how to use this option:
+
+	Enable low and high byte termination on a non-ultra2 controller that
+	is the first aic7xxx controller (the correct bits are 0011), 
+	aic7xxx=override_term:0x3
+
+	Enable all termination on the third aic7xxx controller, high byte
+	termination on the second aic7xxx controller, and low and high byte
+	SE termination on the first aic7xxx controller 
+	(bits are 1111 0010 0011), 
+	aic7xxx=override_term:0xf23
+	
+	No attempt has been made to make this option non-cryptic.  It really
+	shouldn't be used except in dire circumstances, and if that happens,
+	I'm probably going to be telling you what to set this to anyway :)
+
+    "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV
+        bit in the DEVCONFIG PCI register.  Currently, this is one of the
+	very few registers that we have absolutely *no* way of detecting
+	what the variable should be.  It depends entirely on how the chipset
+	and external terminators were coupled by the card/motherboard maker.
+	Further, a chip reset (at power up) always sets this bit to 0.  If
+	there is no BIOS to run on the chipset/card (such as with a 2910C
+	or a motherboard controller with the BIOS totally disabled) then
+	the variable may not get set properly.  Of course, if the proper
+	setting was 0, then that's what it would be after the reset, but if
+	the proper setting is actually 1.....you get the picture.  Now, since
+	we can't detect this at all, I've added this option to force the
+	setting.  If you have a BIOS on your controller then you should never
+	need to use this option.  However, if you are having lots of SCSI
+	reset problems and can't seem to get them knocked out, this may help.
+
+	Here's a test to know for certain if you need this option.  Make
+	a boot floppy that you can use to boot your computer up and that
+	will detect the aic7xxx controller.  Next, power down your computer.
+	While it's down, unplug all SCSI cables from your Adaptec SCSI
+	controller.  Boot the system back up to the Adaptec EZ-SCSI BIOS
+	and then make sure that termination is enabled on your adapter (if
+	you have an Adaptec BIOS of course).  Next, boot up the floppy you
+	made and wait for it to detect the aic7xxx controller.  If the kernel
+	finds the controller fine, says scsi : x hosts and then tries to
+	detect your devices like normal, up to the point where it fails to
+	mount your root file system and panics, then you're fine.  If, on
+	the other hand, the system goes into an infinite reset loop, then
+	you need to use this option and/or the previous option to force the
+	proper termination settings on your controller.   If this happens,
+	then you next need to figure out what your settings should be.
+
+	To find the correct settings, power your machine back down, connect
+	back up the SCSI cables, and boot back into your machine like normal.
+	However, boot with the aic7xxx=verbose:0x39 option.  Record the
+	initial DEVCONFIG values for each of your aic7xxx controllers as
+	they are listed, and also record what the machine is detecting as
+	the proper termination on your controllers.  NOTE: the order in
+	which the initial DEVCONFIG values are printed out is not gauranteed
+	to be the same order as the SCSI controllers are registered.  The
+	above option and this option both work on the order of the SCSI
+	controllers as they are registered, so make sure you match the right
+	DEVCONFIG values with the right controllers if you have more than
+	one aic7xxx controller.
+
+	Once you have the detected termination settings and the initial
+	DEVCONFIG values for each controller, then figure out what the
+	termination on each of the controllers *should* be.  Hopefully, that
+	part is correct, but it could possibly be wrong if there is
+	bogus cable detection logic on your controller or something similar.
+	If all the controllers have the correct termination settings, then
+	don't set the aic7xxx=override_term variable at all, leave it alone.
+	Next, on any controllers that go into an infinite reset loop when
+	you unplug all the SCSI cables, get the starting DEVCONFIG value.
+	If the initial DEVCONFIG value is divisible by 2, then the correct
+	setting for that controller is 0.  If it's an odd number, then
+	the correct setting for that controller is 1.  For any other
+	controllers that didn't have an infinite reset problem, then reverse
+	the above options.  If DEVCONFIG was even, then the correct setting
+	is 1, if not then the correct setting is 0.
+
+	Now that you know what the correct setting was for each controller,
+	we need to encode that into the aic7xxx=stpwlev:0x... variable.
+	This variable is a bit field encoded variable.  Bit 0 is for the first
+	aic7xxx controller, bit 1 for the next, etc.  Put all these bits
+	together and you get a number.  For example, if the third aic7xxx
+	needed a 1, but the second and first both needed a 0, then the bits
+	would be 100 in binary.  This then translates to 0x04.  You would
+	therefore set aic7xxx=stpwlev:0x04.  This is fairly standard binary
+	to hexadecimal conversions here.  If you aren't up to speed on the
+	binary->hex conversion then send an email to the aic7xxx mailing
+	list and someone can help you out.
+
     "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable
         tagged queueing on specific devices.  As of driver version 5.0.6, we
         now globally enable tagged queueing by default, but we also disable
@@ -212,8 +366,8 @@
     
     aic7xxx=verbose,extended,irq_trigger:1
     
-    The only requirement is that individual options be separated by a comma on
-    the command line.
+    The only requirement is that individual options be separated by a comma or
+    a period on the command line.
         
   Module Loading command options
   ------------------------------
@@ -258,14 +412,38 @@
     the system. Each file presents the current configuration and transfer
     statistics (enabled with #define in aic7xxx.c) for each controller.
 
-    Thanks to Michael Neuffer for for his upper-level SCSI help, and
+    Thanks to Michael Neuffer for his upper-level SCSI help, and
     Matthew Jacob for statistics support.
 
+  Debugging the driver
+  ------------------------------
+    Should you have problems with this driver, and would like some help in
+    getting them solved, there are a couple debugging items built into
+    the driver to facilitate getting the needed information from the system.
+    In general, I need a complete description of the problem, with as many
+    logs as possible concerning what happens.  To help with this, there is
+    a command option aic7xxx=panic_on_abort.  This option, when set, forces
+    the driver to panic the kernel on the first SCSI abort issued by the
+    mid level SCSI code.  If your system is going to reset loops and you
+    can't read the screen, then this is what you need.  Not only will it
+    stop the system, but it also prints out a large amount of state
+    information in the process.  Second, if you specify the option
+    "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much
+    information as it runs that you won't be able to see anything.
+    However, this can actually be very usefull if your machine simply
+    locks up when trying to boot, since it will pin-point what was last
+    happening (in regards to the aic7xxx driver) immediately prior to
+    the lockup.  This is really only usefull if your machine simply can
+    not boot up successfully.  If you can get your machine to run, then
+    this will produce far too much information.
+
   FTP sites
   ------------------------------
-    ftp://ftp.dialnet.net/pub/linux/aic7xxx/
+    ftp://ftp.redhat.com/pub/aic/
       - Primary site for Doug Ledford developed driver releases
-      - US Linux mirror of Teleport site
+    ftp://ftp.dialnet.net/pub/linux/aic7xxx
+      - Temporary mirror of the redhat.com ftp site while people
+        get used to the new address
     ftp://ftp.pcnet.com/users/eischen/Linux/
       - Dan Eischen's driver distribution area
     ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.in2000 linux/drivers/scsi/README.in2000
--- v2.0.35/linux/drivers/scsi/README.in2000	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/README.in2000	Sun Nov 15 10:33:06 1998
@@ -1,4 +1,19 @@
 
+UPDATE NEWS: version 1.33 - 26 Aug 98
+
+   Interrupt management in this driver has become, over
+   time, increasingly odd and difficult to explain - this
+   has been mostly due to my own mental inadequacies. In
+   recent kernels, it has failed to function at all when
+   compiled for SMP. I've fixed that problem, and after
+   taking a fresh look at interrupts in general, greatly
+   reduced the number of places where they're fiddled
+   with. Done some heavy testing and it looks very good.
+   The driver now makes use of the __initfunc() and
+   __initdata macros to save about 4k of kernel memory.
+   Once again, the same code works for both 2.0.xx and
+   2.1.xx kernels.
+
 UPDATE NEWS: version 1.32 - 28 Mar 98
 
    Removed the check for legal IN2000 hardware versions:
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.0.35/linux/drivers/scsi/README.ncr53c8xx	Mon Jul 13 13:46:31 1998
+++ linux/drivers/scsi/README.ncr53c8xx	Sun Nov 15 10:33:06 1998
@@ -823,7 +823,7 @@
         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, 
+        read line, prefetch, cache line, write and invalidate, 
         burst 128 (875 only), large dma fifo (875 only), offset 16 (875 only).
 	Can be changed by the following boot setup command:
 		ncr53c8xx=specf:n
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v2.0.35/linux/drivers/scsi/aha152x.c	Sun Nov 15 10:49:43 1998
+++ linux/drivers/scsi/aha152x.c	Sun Nov 15 10:33:07 1998
@@ -551,6 +551,8 @@
 } signatures[] =
 {
   { "Adaptec AHA-1520 BIOS",      0x102e, 21 },  /* Adaptec 152x */
+  { "Adaptec AHA-1520B",            0x0b, 17 },  /* Adaptec 152x rev B */
+  { "Adaptec AHA-1520B/1522B",    0x3e20, 23 },  /* Adaptec 1520B/1522B */
   { "Adaptec ASW-B626 BIOS",      0x1029, 21 },  /* on-board controller */
   { "Adaptec BIOS: ASW-B626",       0x0f, 22 },  /* on-board controller */
   { "Adaptec ASW-B626 S2",        0x2e6c, 19 },  /* on-board controller */
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v2.0.35/linux/drivers/scsi/aha1542.c	Sun Nov 15 10:49:43 1998
+++ linux/drivers/scsi/aha1542.c	Sun Nov 15 10:33:07 1998
@@ -16,6 +16,8 @@
  *  Modified by Mike McLagan <mike.mclagan@linux.org>
  *        Recognise extended mode on AHA1542CP, different bit than 1542CF
  *        1-Jan-97
+ *  Modified by Bjorn L. Thordarson and Einar Thor Einarsson
+ *        Recognize that DMA0 is valid DMA channel -- 13-Jul-98
  */
 
 #include <linux/module.h>
@@ -89,13 +91,6 @@
  *		    Factory default is 5 MB/s.
  */
 
-
-/* The DMA-Controller.  We need to fool with this because we want to 
-   be able to use the aha1542 without having to have the bios enabled */
-#define DMA_MODE_REG	0xd6
-#define DMA_MASK_REG	0xd4
-#define	CASCADE		0xc0
-
 #define BIOS_TRANSLATION_1632 0  /* Used by some old 1542A boards */
 #define BIOS_TRANSLATION_6432 1 /* Default case these days */
 #define BIOS_TRANSLATION_25563 2 /* Big disk case */
@@ -748,8 +743,8 @@
     *dma_chan = 5;
     break;
   case 0x01:
-    printk("DMA priority 0 not available for Adaptec driver\n");
-    return -1;
+    *dma_chan = 0;
+    break;
   case 0:
     /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
        Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
@@ -1019,9 +1014,9 @@
 				    goto unregister;
 			    }
 			    
-			    if (dma_chan >= 5) {
-				    outb((dma_chan - 4) | CASCADE, DMA_MODE_REG);
-				    outb(dma_chan - 4, DMA_MASK_REG);
+			    if (dma_chan == 0 || dma_chan >= 5) {
+				    set_dma_mode(dma_chan, DMA_MODE_CASCADE);
+				    enable_dma(dma_chan);
 			    }
 		    }
 		    aha_host[irq_level - 9] = shpnt;
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg
--- v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.reg	Mon Jul 13 13:46:32 1998
+++ linux/drivers/scsi/aic7xxx/aic7xxx.reg	Sun Nov 15 10:33:07 1998
@@ -1,7 +1,7 @@
 /*
  * Aic7xxx register and scratch ram definitions.
  *
- * Copyright (c) 1994-1997 Justin Gibbs.
+ * Copyright (c) 1994-1998 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -161,6 +161,7 @@
 	access_mode RW
 	bit	WIDEXFER	0x80		/* Wide transfer control */
 	mask	SXFR		0x70		/* Sync transfer rate */
+	mask	SXFR_ULTRA2	0x7f		/* Sync transfer rate */
 	mask	SOFS		0x0f		/* Sync offset */
 }
 
@@ -174,6 +175,13 @@
 	access_mode RW
 	mask	TID		0xf0		/* Target ID mask */
 	mask	OID		0x0f		/* Our ID mask */
+	/*
+	 * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+	 * The aic7890/91 allow an offset of up to 127 transfers in both wide
+	 * and narrow mode.
+	 */
+	alias	SCSIOFFSET
+	mask	SOFS_ULTRA2	0x7f		/* Sync offset U2 chips */
 }
 
 /*
@@ -227,14 +235,15 @@
 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 */
+	bit	TARGET		0x80	/* Board acting as target */
+	bit	SELDO		0x40	/* Selection Done */
+	bit	SELDI		0x20	/* Board has been selected */
+	bit	SELINGO		0x10	/* Selection In Progress */
+	bit	SWRAP		0x08	/* 24bit counter wrap */
+	bit	IOERR		0x08	/* LVD Tranceiver mode changed */
+	bit	SDONE		0x04	/* STCNT = 0x000000 */
+	bit	SPIORDY		0x02	/* SCSI PIO Ready */
+	bit	DMADONE		0x01	/* DMA transfer completed */
 }
 
 /*
@@ -276,6 +285,7 @@
 	address			0x00d
 	access_mode RO
 	bit	OVERRUN		0x80
+	bit	EXP_ACTIVE	0x10	/* SCSI Expander Active */
 	mask	SFCNT		0x1f
 }
 
@@ -290,14 +300,13 @@
 }
 
 /*
- * SCSI Test Control (p. 3-27)
+ * SCSI ID for the aic7890/91 chips
  */
-register SCSITEST {
+register SCSIID_ULTRA2 {
 	address			0x00f
 	access_mode RW
-	bit	RQAKCNT		0x04
-	bit	CNTRTEST	0x02
-	bit	CMODE		0x01
+	mask	TID		0xf0		/* Target ID mask */
+	mask	OID		0x0f		/* Our ID mask */
 }
 
 /*
@@ -312,6 +321,7 @@
 	bit	ENSELDI		0x20
 	bit	ENSELINGO	0x10
 	bit	ENSWRAP		0x08
+	bit	ENIOERR		0x08	/* LVD Tranceiver mode changes */
 	bit	ENSDONE		0x04
 	bit	ENSPIORDY	0x02
 	bit	ENDMADONE	0x01
@@ -424,7 +434,10 @@
 	bit	DIAGLEDON	0x40	/* Aic78X0 only */
 	bit	AUTOFLUSHDIS	0x20
 	bit	SELBUSB		0x08
+	bit	ENAB40		0x08	/* LVD transceiver active */
+	bit	ENAB20		0x04	/* SE/HVD transceiver active */
 	bit	SELWIDE		0x02
+	bit	XCVR		0x01	/* External transceiver active */
 }
 
 /*
@@ -547,6 +560,19 @@
 	bit	ENABLE		0x01
 }
 
+register DSCOMMAND0 {
+	address			0x084
+	access_mode RW
+	bit	CACHETHEN	0x80
+	bit	DPARCKEN	0x40
+	bit	MPARCKEN	0x20
+	bit	EXTREQLCK	0x10
+	bit	INTSCBRAMSEL	0x08
+	bit	RAMPS		0x04
+	bit	USCBSIZE32	0x02
+	bit	CIOPARCKEN	0x01
+}
+
 /*
  * On the aic78X0 chips, Board Control is replaced by the DSCommand
  * register (p. 4-64)
@@ -676,6 +702,7 @@
 register ERROR {
 	address			0x092
 	access_mode RO
+	bit	CIOPARERR	0x80	/* Ultra2 only */
 	bit	PCIERRSTAT	0x40	/* PCI only */
 	bit	MPARERR		0x20	/* PCI only */
 	bit	DPARERR		0x10	/* PCI only */
@@ -701,6 +728,7 @@
 register DFCNTRL {
 	address			0x093
 	access_mode RW
+	bit	PRELOADEN	0x80	/* aic7890 only */
 	bit	WIDEODD		0x40
 	bit	SCSIEN		0x20
 	bit	SDMAEN		0x10
@@ -715,6 +743,7 @@
 register DFSTATUS {
 	address			0x094
 	access_mode RO
+	bit	PRELOAD_AVAIL	0x80
 	bit	DWORDEMP	0x20
 	bit	MREQPEND	0x10
 	bit	HDONE		0x08
@@ -777,6 +806,14 @@
 }
 
 /*
+ * Special Function
+ */
+register SFUNCT {
+	address			0x09f
+	access_mode RW
+}
+
+/*
  * SCB Definition (p. 5-4)
  */
 scb {
@@ -865,6 +902,109 @@
 
 register DSPCISTATUS {
 	address			0x086
+	mask	DFTHRSH_100	0xc0
+}
+
+register CCHADDR {
+	address			0x0E0
+	size 8
+}
+
+register CCHCNT {
+	address			0x0E8
+}
+
+register CCSGRAM {
+	address			0x0E9
+}
+
+register CCSGADDR {
+	address			0x0EA
+}
+
+register CCSGCTL {
+	address			0x0EB
+	bit	CCSGDONE	0x80
+	bit	CCSGEN		0x08
+	bit	FLAG		0x02
+	bit	CCSGRESET	0x01
+}
+
+register CCSCBCNT {
+	address			0xEF
+}
+
+register CCSCBCTL {
+	address			0x0EE
+	bit	CCSCBDONE	0x80
+	bit	ARRDONE		0x40	/* SCB Array prefetch done */
+	bit	CCARREN		0x10
+	bit	CCSCBEN		0x08
+	bit	CCSCBDIR	0x04
+	bit	CCSCBRESET	0x01
+}
+
+register CCSCBADDR {
+	address			0x0ED
+}
+
+register CCSCBRAM {
+	address			0xEC
+}
+
+register CCSCBPTR {
+	address			0x0F1
+}
+
+register HNSCB_QOFF {
+	address			0x0F4
+}
+
+register SNSCB_QOFF {
+	address			0x0F6
+}
+
+register SDSCB_QOFF {
+	address			0x0F8
+}
+
+register QOFF_CTLSTA {
+	address			0x0FA
+	bit	SCB_AVAIL	0x40
+	bit	SNSCB_ROLLOVER	0x20
+	bit	SDSCB_ROLLOVER	0x10
+	mask	SCB_QSIZE	0x07
+	mask	SCB_QSIZE_256	0x06
+}
+
+register DFF_THRSH {
+	address			0x0FB
+	mask	WR_DFTHRSH	0x70
+	mask	RD_DFTHRSH	0x07
+	mask	RD_DFTHRSH_MIN	0x00
+	mask	RD_DFTHRSH_25	0x01
+	mask	RD_DFTHRSH_50	0x02
+	mask	RD_DFTHRSH_63	0x03
+	mask	RD_DFTHRSH_75	0x04
+	mask	RD_DFTHRSH_85	0x05
+	mask	RD_DFTHRSH_90	0x06
+	mask	RD_DFTHRSH_MAX	0x07
+	mask	WR_DFTHRSH_MIN	0x00
+	mask	WR_DFTHRSH_25	0x10
+	mask	WR_DFTHRSH_50	0x20
+	mask	WR_DFTHRSH_63	0x30
+	mask	WR_DFTHRSH_75	0x40
+	mask	WR_DFTHRSH_85	0x50
+	mask	WR_DFTHRSH_90	0x60
+	mask	WR_DFTHRSH_MAX	0x70
+}
+
+register SG_CACHEPTR {
+	access_mode RW
+	address			0x0fc
+	mask	SG_USER_DATA	0xfc
+	bit	LAST_SEG	0x02
+	bit	LAST_SEG_DONE	0x01
 }
 
 register BRDCTL	{
@@ -877,6 +1017,12 @@
 	bit	BRDRW		0x04
 	bit	BRDCTL1		0x02
 	bit	BRDCTL0		0x01
+	/* 7890 Definitions */
+	bit	BRDDAT4		0x10
+	bit	BRDDAT3		0x08
+	bit	BRDDAT2		0x04
+	bit	BRDRW_ULTRA2	0x02
+	bit	BRDSTB_ULTRA2	0x01
 }
 
 /*
@@ -935,7 +1081,7 @@
 	/*
 	 * 1 byte per target starting at this address for configuration values
 	 */
-	TARG_SCRATCH {
+	TARG_SCSIRATE {
 		size		16
 	}
 	/*
@@ -960,6 +1106,7 @@
 	/* Parameters for DMA Logic */
 	DMAPARAMS {
 		size		1
+		bit	PRELOADEN	0x80
 		bit	WIDEODD		0x40
 		bit	SCSIEN		0x20
 		bit	SDMAEN		0x10
@@ -1078,12 +1225,27 @@
 		mask	MSGOUT_PHASEMIS	0x10
 		alias	RETURN_1
 	}
+	ARG_2 {
+		size		1
+		alias	RETURN_2
+	}
+
 	/*
 	 * Snapshot of MSG_OUT taken after each message is sent.
 	 */
 	LAST_MSG {
 		size		1
 	}
+
+	/*
+	 * Number of times we have filled the CCSGRAM with prefetched
+	 * SG elements.
+	 */
+	PREFETCH_CNT {
+		size		1
+	}
+
+
 	/*
 	 * These are reserved registers in the card's scratch ram.  Some of
 	 * the values are specified in the AHA2742 technical reference manual
@@ -1108,22 +1270,35 @@
 		mask	BIOSDISABLED		0x30	
 		bit	CHANNEL_B_PRIMARY	0x08
 	}
+	/*
+	 * Per target SCSI offset values for Ultra2 controllers.
+	 */
+	TARG_OFFSET {
+		address		0x070
+		size		16
+	}
 }
 
 const SCB_LIST_NULL	0xff
 
+const CCSGADDR_MAX	0x80
+const CCSGRAM_MAXSEGS	16
+
 /* Offsets into the SCBID array where different data is stored */
 const UNTAGGEDSCB_OFFSET	0
 const QOUTFIFO_OFFSET		1
 const QINFIFO_OFFSET		2
 
 /* WDTR Message values */
-const BUS_8_BIT		0x00
+const BUS_8_BIT			0x00
 const BUS_16_BIT		0x01
 const BUS_32_BIT		0x02
+
+/* Offset maximums */
 const MAX_OFFSET_8BIT		0x0f
-const MAX_OFFSET_16BIT	0x08
-const HOST_MSG		0xFF
+const MAX_OFFSET_16BIT		0x08
+const MAX_OFFSET_ULTRA2		0x7f
+const HOST_MSG			0xff
 
 /* Target mode command processing constants */
 const CMD_GROUP_CODE_SHIFT	0x05
@@ -1135,11 +1310,7 @@
 /*
  * Downloaded (kernel inserted) constants
  */
-/*
- * Mask of bits to test against when looking at the Queue Count
- * registers.  Works around a bug on aic7850 chips. 
- */
-const QCNTMASK		download
+
 /*
  * Number of command descriptors in the command descriptor array.
  */
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
--- v2.0.35/linux/drivers/scsi/aic7xxx/aic7xxx.seq	Mon Jul 13 13:46:34 1998
+++ linux/drivers/scsi/aic7xxx/aic7xxx.seq	Sun Nov 15 10:33:07 1998
@@ -1,7 +1,7 @@
 /*
  * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
  *
- * Copyright (c) 1994-1997 Justin Gibbs.
+ * Copyright (c) 1994-1998 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -14,7 +14,7 @@
  *    derived from this software without specific prior written permission.
  *
  * Where this Software is combined with software released under the terms of 
- * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * the GNU 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
@@ -32,7 +32,7 @@
  * 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 $
+ *	$Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
  */
 
 #include "aic7xxx.reg"
@@ -59,38 +59,55 @@
 reset:
 	clr	SCSISIGO;		/* De-assert BSY */
 	/* Always allow reselection */
-.if ( TARGET_MODE )
-	mvi	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
-.else
-	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
-.endif
+	if ((p->flags & AHC_TARGETMODE) != 0) {
+		mvi	SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
+	} else {
+		mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
+	}
+
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/* Ensure that no DMA operations are in progress */
+		clr	CCSGCTL;
+		clr	CCSCBCTL;
+	}
+
 	call	clear_target_state;
 	and	SXFRCTL0, ~SPIOEN;
 poll_for_work:
-	mov	A, QINPOS;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		mov	A, QINPOS;
+	}
 poll_for_work_loop:
-	and	SEQCTL, ~PAUSEDIS;
-	test	SSTAT0, SELDO|SELDI	jnz selection;
-	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 */
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		and	SEQCTL, ~PAUSEDIS;
+	}
 	test	SSTAT0, SELDO|SELDI	jnz selection;
 	test	SCSISEQ, ENSELO	jnz poll_for_work;
-	xor	SBLKCTL,SELBUSB;	/* Toggle back */
-.endif
+	if ((p->features & AHC_TWIN) != 0) {
+		/*
+		 * Twin channel devices cannot handle things like SELTO
+		 * interrupts on the "background" channel.  So, if we
+		 * are selecting, keep polling the current channel util
+		 * either a selection or reselection occurs.
+		 */
+		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+		test	SSTAT0, SELDO|SELDI	jnz selection;
+		test	SCSISEQ, ENSELO	jnz poll_for_work;
+		xor	SBLKCTL,SELBUSB;	/* Toggle back */
+	}
 	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
 test_queue:
 	/* Has the driver posted any work for us? */
-	or	SEQCTL, PAUSEDIS;
-	cmp	KERNEL_QINPOS, A je poll_for_work_loop;
-	inc	QINPOS;
-	and	SEQCTL, ~PAUSEDIS;
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+		mov	NONE, SNSCB_QOFF;
+		inc	QINPOS;
+	} else {
+		or	SEQCTL, PAUSEDIS;
+		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
+		inc	QINPOS;
+		and	SEQCTL, ~PAUSEDIS;
+	}
 
 /*
  * We have at least one queued SCB now and we don't have any 
@@ -98,26 +115,24 @@
  * 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;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	ALLZEROS	call	get_free_or_disc_scb;
+	}
+
 dequeue_scb:
 	add	A, -1, QINPOS;
-	mvi	QINFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+	mvi	QINFIFO_OFFSET call fetch_byte;
 
-	call	dma_finish;
-	mov	SINDEX, DFDAT;
-.if !( SCB_PAGING )
-	/* In the non-paging case, the SCBID == hardware SCB index */
-	mov	SCBPTR, SINDEX;
-.endif
+	if ((p->flags & AHC_PAGESCBS) == 0) {
+		/* In the non-paging case, the SCBID == hardware SCB index */
+		mov	SCBPTR, RETURN_2;
+	}
 dma_queued_scb:
 /*
  * DMA the SCB from host ram into the current SCB location.
  */
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
-	call	dma_scb;
+	mov	RETURN_2	 call dma_scb;
 
 start_scb:
 	/*
@@ -135,16 +150,22 @@
 	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
+	if ((p->features & AHC_TWIN) != 0) {
+		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
+		or	SINDEX,A;
+		mov	SBLKCTL,SINDEX;		/* select channel */
+	}
 initialize_scsiid:
-	and	A, TID, SCB_TCL;	/* Get target ID */
-	and	SCSIID, OID;		/* Clear old target */
-	or	SCSIID, A;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID_ULTRA2, OID;	/* Clear old target */
+		or	SCSIID_ULTRA2, A;
+	} else {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID, OID;		/* Clear old target */
+		or	SCSIID, A;
+	}
 	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
 
 /*
@@ -155,143 +176,154 @@
 initialize_channel:
 	or	A, CLRSTCNT|CLRCHN, SINDEX;
 	or	SXFRCTL0, A;
-.if ( ULTRA )
+	if ((p->features & AHC_ULTRA) != 0) {
 ultra:
-	mvi	SINDEX, ULTRA_ENB+1;
-	test	SAVED_TCL, 0x80		jnz ultra_2;	/* Target ID > 7 */
-	dec	SINDEX;
+		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
- 
+		mov     FUNCTION1,SAVED_TCL;
+		mov     A,FUNCTION1;
+		test	SINDIR, A	jz ndx_dtr;
+		or	SXFRCTL0, FAST20;
+	} 
 /*
  * Initialize SCSIRATE with the appropriate value for this target.
  * The SCSIRATE settings for each target are stored in an array
- * based at TARG_SCRATCH.
+ * based at TARG_SCSIRATE.
  */
 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 */
+	if ((p->features & AHC_TWIN) != 0) {
+		test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
+		or	SAVED_TCL, SELBUSB; 
+		or	A,0x08;			/* Channel B entries add 8 */
 ndx_dtr_2:
-	add	SINDEX,TARG_SCRATCH,A;
+	}
+
+	if ((p->features & AHC_ULTRA2) != 0) {
+		add	SINDEX, TARG_OFFSET, A;
+		mov	SCSIOFFSET, SINDIR;
+	}
+
+	add	SINDEX,TARG_SCSIRATE,A;
 	mov	SCSIRATE,SINDIR ret;
 
 
 selection:
 	test	SSTAT0,SELDO	jnz select_out;
 select_in:
-.if ( TARGET_MODE )
-	test	SSTAT0, TARGET	jz initiator_reselect;
-	/*
-	 * We've just been selected.  Assert BSY and
-	 * setup the phase for receiving the messages
-	 * from the target.
-	 */
-	mvi	SCSISIGO, P_MESGOUT|BSYO;
-	mvi	CLRSINT0, CLRSELDO;
-
-	/*
-	 * If ATN isn't asserted, go directly to bus free.
-	 */
-	test	SCSISIGI, ATNI	jz	target_busfree;
-
-	/*
-	 * Setup the DMA for sending the identify and
-	 * command information.
-	 */
-	mov	A, TMODE_CMDADDR_NEXT;
-	mvi	TMODE_CMDADDR call set_32byte_haddr_and_clrcnt;
-	mvi	DFCNTRL, FIFORESET;
+	if ((p->flags & AHC_TARGETMODE) != 0) {
+		test	SSTAT0, TARGET	jz initiator_reselect;
+		/*
+		 * We've just been selected.  Assert BSY and
+		 * setup the phase for receiving the messages
+		 * from the target.
+		 */
+		mvi	SCSISIGO, P_MESGOUT|BSYO;
+		mvi	CLRSINT0, CLRSELDO;
+
+		/*
+		 * If ATN isn't asserted, go directly to bus free.
+		 */
+		test	SCSISIGI, ATNI	jz	target_busfree;
+
+		/*
+		 * Setup the DMA for sending the identify and
+		 * command information.
+		 */
+		mov	A, TMODE_CMDADDR_NEXT;
+		mvi	DINDEX, HADDR;
+		mvi	TMODE_CMDADDR call set_32byte_addr;
+		mvi	DFCNTRL, FIFORESET;
 
-	clr	SINDEX;
-	/* Watch ATN closely now */
+		clr	SINDEX;
+		/* Watch ATN closely now */
 message_loop:
-	or	SXFRCTL0, SPIOEN;
-	test	SSTAT0, SPIORDY jz .;
-	and	SXFRCTL0, ~SPIOEN;
-	mov	DINDEX, SCSIDATL;
-	mov	DFDAT, DINDEX;
-	inc	SINDEX;
-
-	/* Message Testing... */
-	test	DINDEX, MSG_IDENTIFYFLAG jz . + 2;
-	mov	ARG_1, DINDEX;
-
-	test	SCSISIGI, ATNI	jnz	message_loop;
-	add	A, -4, SINDEX;
-	jc	target_cmdphase;
-	mvi	DFDAT, SCB_LIST_NULL;	/* Terminate the message list */
+		or	SXFRCTL0, SPIOEN;
+		test	SSTAT0, SPIORDY jz .;
+		and	SXFRCTL0, ~SPIOEN;
+		mov	DINDEX, SCSIDATL;
+		mov	DFDAT, DINDEX;
+		inc	SINDEX;
+
+		/* Message Testing... */
+		test	DINDEX, MSG_IDENTIFYFLAG jz . + 2;
+		mov	ARG_1, DINDEX;
+
+		test	SCSISIGI, ATNI	jnz	message_loop;
+		add	A, -4, SINDEX;
+		jc	target_cmdphase;
+		mvi	DFDAT, SCB_LIST_NULL;	/* Terminate the message list */
 
 target_cmdphase:
-	add	HCNT[0], 1, A;
-	mvi	SCSISIGO, P_COMMAND|BSYO;
-	or	SXFRCTL0, SPIOEN;
-	test	SSTAT0, SPIORDY jz .;
-	mov	A, SCSIDATL;
-	mov	DFDAT, A;	/* Store for host */
-
-	/*
-	 * Determine the number of bytes to read
-	 * based on the command group code.  Count is
-	 * one less than the total since we've already
-	 * fetched the first byte.
-	 */
-	clr	SINDEX;
-	shr	A, CMD_GROUP_CODE_SHIFT;
-	add	SEQADDR0, A;
-
-	add	SINDEX, CMD_GROUP0_BYTE_DELTA;
-	nop;	/* Group 1 and 2 are the same */
-	add	SINDEX, CMD_GROUP2_BYTE_DELTA;
-	nop;	/* Group 3 is reserved */
-	add	SINDEX, CMD_GROUP4_BYTE_DELTA;
-	add	SINDEX, CMD_GROUP5_BYTE_DELTA;
+		add	HCNT[0], 1, A;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	SCSISIGO, P_COMMAND|BSYO;
+		or	SXFRCTL0, SPIOEN;
+		test	SSTAT0, SPIORDY jz .;
+		mov	A, SCSIDATL;
+		mov	DFDAT, A;	/* Store for host */
+
+		/*
+		 * Determine the number of bytes to read
+		 * based on the command group code.  Count is
+		 * one less than the total since we've already
+		 * fetched the first byte.
+		 */
+		clr	SINDEX;
+		shr	A, CMD_GROUP_CODE_SHIFT;
+		add	SEQADDR0, A;
+
+		add	SINDEX, CMD_GROUP0_BYTE_DELTA;
+		nop;	/* Group 1 and 2 are the same */
+		add	SINDEX, CMD_GROUP2_BYTE_DELTA;
+		nop;	/* Group 3 is reserved */
+		add	SINDEX, CMD_GROUP4_BYTE_DELTA;
+		add	SINDEX, CMD_GROUP5_BYTE_DELTA;
 		/* Group 6 and 7 are not handled yet */
 
-	mov	A, SINDEX;
-	add	HCNT[0], A;
+		mov	A, SINDEX;
+		add	HCNT[0], A;
 
 command_loop:
-	test	SSTAT0, SPIORDY jz .;
-	cmp	SINDEX, 1 jne . + 2;
-	and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
-	mov	DFDAT, SCSIDATL;
-	dec	SINDEX;
-	test	SINDEX, 0xFF jnz command_loop;
+		test	SSTAT0, SPIORDY jz .;
+		cmp	SINDEX, 1 jne . + 2;
+		and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
+		mov	DFDAT, SCSIDATL;
+		dec	SINDEX;
+		test	SINDEX, 0xFF jnz command_loop;
 
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
 	
-	call	dma_finish;
+		call	dma_finish;
 
-	test	ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post;
+		test	ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post;
 
-	mvi	SCSISIGO, P_MESGIN|BSYO;
+		mvi	SCSISIGO, P_MESGIN|BSYO;
 
-	or	SXFRCTL0, SPIOEN;
+		or	SXFRCTL0, SPIOEN;
 
-	mvi	MSG_DISCONNECT call target_outb;
+		mvi	MSG_DISCONNECT call target_outb;
 	
 selectin_post:
-	inc	TMODE_CMDADDR_NEXT;
-	cmp	TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
-	clr	TMODE_CMDADDR_NEXT;
-	mvi	QOUTFIFO, SCB_LIST_NULL;
-	mvi	INTSTAT,CMDCMPLT;
+		inc	TMODE_CMDADDR_NEXT;
+		cmp	TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
+		clr	TMODE_CMDADDR_NEXT;
+		mvi	QOUTFIFO, SCB_LIST_NULL;
+		mvi	INTSTAT,CMDCMPLT;
 
-	test	ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree;
+		test	ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree;
 
-	/* Busy loop on something then go to data or status phase */
+		/* Busy loop on something then go to data or status phase */
 
 target_busfree:
-	clr	SCSISIGO;
-	jmp	poll_for_work;
+		clr	SCSISIGO;
+		jmp	poll_for_work;
+
+	}
 
-.endif /* TARGET_MODE */
 /*
  * 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.
@@ -396,26 +428,41 @@
 	jmp	data_phase_loop;
 
 p_data:
-	mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+	} else {
+		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	}
 	test	LASTPHASE, IOI jnz . + 2;
 	or	DMAPARAMS, DIRECTION;
 	call	assert;			/*
 					 * Ensure entering a data
 					 * phase is okay - seen identify, etc.
 					 */
-
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	CCSGADDR, CCSGADDR_MAX;
+	}
 	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
 
+	/* We have seen a data phase */
+	or	SEQ_FLAGS, DPHASE;
+
 	/*
 	 * Initialize the DMA address and counter from the SCB.
 	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
 	 * modify the values in the SCB itself until we see a
 	 * save data pointers message.
 	 */
-	mvi	DINDEX, HADDR;
-	mvi	SCB_DATAPTR	call bcopy_7;
-
-	call	set_stcnt_from_hcnt;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_DATAPTR, 7;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_DATAPTR	call bcopy_7;
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		call	set_stcnt_from_hcnt;
+	}
 
 	mov	SG_COUNT,SCB_SGCOUNT;
 
@@ -432,19 +479,38 @@
  * had an overrun.
  */
 	or	SXFRCTL1,BITBUCKET;
-	mvi	HCNT[0], 0xff;
-	mvi	HCNT[1], 0xff;
-	mvi	HCNT[2], 0xff;
-	call	set_stcnt_from_hcnt;
 	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
-
+	if ((p->features & AHC_ULTRA2) != 0) {
+		bmov	HCNT, ALLONES, 3;
+	} else {
+		mvi	STCNT[0], 0xFF;
+		mvi	STCNT[1], 0xFF;
+		mvi	STCNT[2], 0xFF;
+	}
 data_phase_inbounds:
-/* If we are the last SG block, ensure wideodd is off. */
+/* If we are the last SG block, tell the hardware. */
 	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
-	and	DMAPARAMS, ~WIDEODD;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SG_CACHEPTR, LAST_SEG;
+	} else {
+		and	DMAPARAMS, ~WIDEODD;
+	}
 data_phase_wideodd:
-	mov	DMAPARAMS  call dma;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mov	SINDEX, ALLONES;
+		mov	DFCNTRL, DMAPARAMS;
+		test	SSTAT0, SDONE jnz .;/* Wait for preload to complete */
+data_phase_dma_loop:
+		test	SSTAT0,	SDONE jnz data_phase_dma_done;
+		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
+data_phase_dma_phasemis:
+		test	SSTAT0,SDONE	jnz . + 2;
+		mov	SINDEX,ALLZEROS;	/* Remeber the phasemiss */
+	} else {
+		mov	DMAPARAMS  call dma;
+	}
 
+data_phase_dma_done:
 /* Go tell the host about any overruns */
 	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
 
@@ -458,11 +524,6 @@
 	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
@@ -471,33 +532,71 @@
  * 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;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/*
+		 * Do we have any prefetch left???
+		 */
+		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
+
+		/*
+		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
+		 */
+		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
+		mvi	A, CCSGADDR_MAX;
+		jc	. + 2;
+		shl	A, 3, SG_COUNT;
+		mov	CCHCNT, A;
+		bmov	CCHADDR, SG_NEXT, 4;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		and	CCSGCTL, ~CCSGEN;
+		test	CCSGCTL, CCSGEN jnz .;
+		mvi	CCSGCTL, CCSGRESET;
+prefetched_segs_avail:
+		bmov 	HADDR, CCSGRAM, 8;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SG_NEXT	call bcopy_4;
+
+		mvi	HCNT[0],SG_SIZEOF;
+		clr	HCNT[1];
+		clr	HCNT[2];
+
+		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+		call	dma_finish;
+
+		/*
+		 * Copy data from FIFO into SCB data pointer and data count.
+		 * This assumes that the SG segments are of the form:
+		 * struct ahc_dma_seg {
+		 *	u_int32_t	addr;	four bytes, little-endian order
+		 *	u_int32_t	len;	four bytes, little endian order
+		 * };
+		 */
+		mvi	HADDR	call dfdat_in_7;
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		/* Load STCNT as well.  It is a mirror of HCNT */
+		call	set_stcnt_from_hcnt;
+	}
 
-/*
- * 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;
+/* Advance the SG pointer */
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SG_NEXT[0],SG_SIZEOF;
+	adc	SG_NEXT[1],A;
 
-/* Load STCNT as well.  It is a mirror of HCNT */
-	call	set_stcnt_from_hcnt;
 	test	SSTAT1,PHASEMIS	jz data_phase_loop;
+	/* Ensure the last seg is visable at the shaddow layer */
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	DFCNTRL, PRELOADEN;
+	}
 
 data_phase_finish:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+	}
 /*
  * 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 
@@ -508,12 +607,17 @@
 	mov	SCB_RESID_DCNT[2],STCNT[2];
 	mov	SCB_RESID_SGCNT, SG_COUNT;
 
-	/* We have seen a data phase */
-	or	SEQ_FLAGS, DPHASE;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
 
 	jmp	ITloop;
 
 data_phase_overrun:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
 /*
  * Turn off BITBUCKET mode and notify the host
  */
@@ -521,6 +625,19 @@
 	mvi	INTSTAT,DATA_OVERRUN;
 	jmp	ITloop;
 
+ultra2_dmafinish:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
+		and	DFCNTRL, ~SCSIEN;
+		test	DFCNTRL, SCSIEN jnz .;
+		or	DFCNTRL, FIFOFLUSH;
+		test	DFSTATUS, FIFOEMP jz . - 1;
+ultra2_dmahalt:
+		and     DFCNTRL, ~(SCSIEN|HDMAEN);
+		test	DFCNTRL, HDMAEN jnz .;
+		ret;
+	}
+
 /*
  * Command phase.  Set up the DMA registers and let 'er rip.
  */
@@ -530,14 +647,26 @@
 /*
  * 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;
+	if ((p->features & AHC_ULTRA2) != 0) {
+		or	SG_CACHEPTR, LAST_SEG;
+	}
+
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_CMDPTR, 5;
+		bmov	HCNT[1], ALLZEROS, 2;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_CMDPTR	call bcopy_5;
+		clr	HCNT[1];
+		clr	HCNT[2];
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		call	set_stcnt_from_hcnt;
+		mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	} else {
+		mvi	(PRELOADEN|SCSIEN|HDMAEN|DIRECTION) call dma;
+	}
 	jmp	ITloop;
 
 /*
@@ -562,7 +691,6 @@
  * on an SCB that might not be for the current nexus. (For example, a
  * BDR message in responce to a bad reselection would leave us pointed to
  * an SCB that doesn't have anything to do with the current target).
-
  * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
  * bus device reset).
  *
@@ -574,11 +702,11 @@
 	mov	SINDEX, MSG_OUT;
 	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
 p_mesgout_identify:
-.if ( WIDE )
-	and	SINDEX,0xf,SCB_TCL;	/* lun */
-.else
-	and	SINDEX,0x7,SCB_TCL;	/* lun */
-.endif
+	if ((p->features & AHC_WIDE) != 0) {
+		and	SINDEX,0xf,SCB_TCL;	/* lun */
+	} else {
+		and	SINDEX,0x7,SCB_TCL;	/* lun */
+	}
 	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
 	or	SINDEX,A;		/* or in disconnect privledge */
 	or	SINDEX,MSG_IDENTIFYFLAG;
@@ -606,6 +734,7 @@
 p_mesgout_from_host:
 	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
 	mvi     INTSTAT,AWAITING_MSG;
+	nop;
 	/*
 	 * Did the host detect a phase change?
 	 */
@@ -692,6 +821,7 @@
 check_status:
 	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
 	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
+	nop;
 	cmp	RETURN_1, SEND_SENSE	jne complete;
 	/* This SCB becomes the next to execute as it will retrieve sense */
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
@@ -710,21 +840,21 @@
 	/* If we are untagged, clear our address up in host ram */
 	test	SCB_CONTROL, TAG_ENB jnz complete_post;
 	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, FIFORESET;
-	mvi	DFDAT, SCB_LIST_NULL;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
-	call	dma_finish;
+	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
+	mvi	SCB_LIST_NULL call post_byte;
 
 complete_post:
 	/* Post the SCB and issue an interrupt */
-	mov	A, QOUTPOS;
-	mvi	QOUTFIFO_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, FIFORESET;
-	mov	DFDAT, SCB_TAG;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
-	call	dma_finish;
-	inc 	QOUTPOS;
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		mov	A, SDSCB_QOFF;
+	} else {
+		mov	A, QOUTPOS;
+	}
+	mvi	QOUTFIFO_OFFSET call post_byte_setup;
+	mov	SCB_TAG call post_byte;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		inc 	QOUTPOS;
+	}
 	mvi	INTSTAT,CMDCMPLT;
 
 add_to_free_list:
@@ -796,18 +926,19 @@
  * clearing the "disconnected" bit so we don't "find" it by accident later.
  */
 mesgin_identify:
-.if ( WIDE )
-	and	A,0x0f;			/* lun in lower four bits */
-.else
-	and	A,0x07;			/* lun in lower three bits */
-.endif
+	
+	if ((p->features & AHC_WIDE) != 0) {
+		and	A,0x0f;		/* lun in lower four bits */
+	} else {
+		and	A,0x07;		/* lun in lower three bits */
+	}
 	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
 
 	call	get_untagged_SCBID;
 	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
-.if ( SCB_PAGING )
-	test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
+	}
 	/*
 	 * If the SCB was found in the disconnected list (as is
 	 * always the case in non-paging scenarios), SCBPTR is already
@@ -833,19 +964,19 @@
 get_tag:
 	mvi	ARG_1	call inb_next;	/* tag value */
 
-.if ! ( SCB_PAGING )
+	if ((p->flags & AHC_PAGESCBS) == 0) {
 index_by_tag:
-	mov	SCBPTR,ARG_1;
-	test	SCB_CONTROL,TAG_ENB	jz  not_found;
-	mov	SCBPTR	call rem_scb_from_disc_list;
-.else
-/*
- * Ensure that the SCB the tag points to is for an SCB transaction
- * to the reconnecting target.
- */
+		mov	SCBPTR,ARG_1;
+		test	SCB_CONTROL,TAG_ENB	jz  not_found;
+		mov	SCBPTR	call rem_scb_from_disc_list;
+	} else {
+		/*
+		 * Ensure that the SCB the tag points to is for
+		 * an SCB transaction to the reconnecting target.
+		 */
 use_retrieveSCB:
-	call	retrieveSCB;
-.endif
+		call	retrieveSCB;
+	}
 setup_SCB:
 	mov	A, SAVED_TCL;
 	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
@@ -924,16 +1055,16 @@
 inb_last:
 	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
 
-.if ( TARGET_MODE )
-/*
- * Send a byte to an initiator in Automatic PIO mode.
- * SPIOEN must be on prior to calling this routine.
- */
+if ((p->flags & AHC_TARGETMODE) != 0) {
+	/*
+	 * Send a byte to an initiator in Automatic PIO mode.
+	 * SPIOEN must be on prior to calling this routine.
+	 */
 target_outb:
 	mov	SCSIDATL, SINDEX;
 	test	SSTAT0, SPIORDY	jz .;
 	ret;
-.endif
+}
 	
 mesgin_phasemis:
 /*
@@ -980,7 +1111,17 @@
 dma_dmadone:
 	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
 dma_halt:
-	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN)	jnz dma_halt; 
+	/*
+	 * Some revisions of the aic7880 have a problem where, if the
+	 * data fifo is full, but the PCI input latch is not empty, 
+	 * HDMAEN cannot be cleared.  The fix used here is to attempt
+	 * to drain the data fifo until there is space for the input
+	 * latch to drain and HDMAEN de-asserts.
+	 */
+	if ((p->features & AHC_ULTRA2) == 0) {
+		mov	NONE, DFDAT;
+	}
+	test	DFCNTRL, HDMAEN jnz dma_halt;
 return:
 	ret;
 
@@ -1079,18 +1220,67 @@
 	mov	ARG_1, SCB_TAG	ret;
 	mvi	ARG_1, SCB_LIST_NULL ret;
 
-set_SCBID_host_addr_and_cnt:
-	mov	DINDEX, SINDEX;
-	mvi	SCBID_ADDR call	set_1byte_haddr_and_clrcnt;
-	mvi	HCNT[0], 1 ret;
+/*
+ * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
+ * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
+ */
+fetch_byte:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		mvi	CCSGCTL, CCSGRESET;
+		bmov	RETURN_2, CCSGRAM, 1 ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+		call	dma_finish;
+		mov	RETURN_2, DFDAT ret;
+	}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
+ */
+post_byte_setup:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSCBCTL, CCSCBRESET ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, FIFORESET ret;
+	}
+
+post_byte:
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	CCSCBRAM, SINDEX, 1;
+		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+		test	CCSCBCTL, CCSCBDONE jz .;
+		clr	CCSCBCTL ret;
+	} else {
+		mov	DFDAT, SINDEX;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		jmp	dma_finish;
+	}
 
 get_SCBID_from_host:
 	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call set_SCBID_host_addr_and_cnt;
-	mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-
-	call	dma_finish;
-	mov	ARG_1, DFDAT ret;
+	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
+	mov	RETURN_1,  RETURN_2 ret;
 
 phase_lock:     
 	test	SSTAT1, REQINIT jz phase_lock;
@@ -1116,65 +1306,102 @@
 	mov	DINDIR, SINDIR ret;
 
 /*
- * Setup haddr and count assuming that A is an
- * index into an array of 32byte objects.
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
  */
-set_32byte_haddr_and_clrcnt:
-	shr	DINDEX, 3, A;
+set_32byte_addr:
+	shr	ARG_2, 3, A;
 	shl	A, 5;
-set_1byte_haddr_and_clrcnt:	/* DINDEX must be 0 upon call */
-	add	HADDR[0], A, SINDIR;
-	mov	A, DINDEX;
-	adc	HADDR[1], A, SINDIR;
+/*
+ * Setup addr assuming that A + (ARG_1 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+	add	DINDIR, A, SINDIR;
+	mov	A, ARG_2;
+	adc	DINDIR, A, SINDIR;
 	clr	A;
-	adc	HADDR[2], A, SINDIR;
-	adc	HADDR[3], A, SINDIR;
-	/* Clear Count */
-	clr	HCNT[1];
-	clr	HCNT[2] ret;
+	adc	DINDIR, A, SINDIR;
+	adc	DINDIR, A, SINDIR ret;
 
+/*
+ * Either post or fetch and SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
 dma_scb:
-	/*
-	 * SCB index is in SINDEX.  Determine the physical address in
-	 * the host where this SCB is located and load HADDR with it.
-	 */
 	mov	A, SINDEX;
-	mvi	HSCB_ADDR call set_32byte_haddr_and_clrcnt;
-	mvi	HCNT[0], 28;
-	mov	DFCNTRL, DMAPARAMS;
-	test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
-	/* Fill it with the SCB data */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mov	CCSCBPTR, SCBPTR;
+		mvi	CCHCNT, 32;
+		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
+		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+		jmp	dma_scb_finish;
+dma_scb_tohost:
+		if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+			mvi	CCSCBCTL, CCSCBRESET;
+			bmov	CCSCBRAM, SCB_CONTROL, 32;
+			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			test	CCSCBCTL, CCSCBDONE jz .;
+		} else {
+			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+		}
+dma_scb_finish:
+		clr	CCSCBCTL;
+		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
+		ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mvi	HCNT[0], 32;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mov	DFCNTRL, DMAPARAMS;
+		test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
+		/* Fill it with the SCB data */
 copy_scb_tofifo:
-	mvi	SINDEX, SCB_CONTROL;
-	add	A, 28, SINDEX;
+		mvi	SINDEX, SCB_CONTROL;
+		add	A, 32, SINDEX;
 copy_scb_tofifo_loop:
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	mov	DFDAT,SINDIR;
-	cmp	SINDEX, A jne copy_scb_tofifo_loop;
-	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		mov	DFDAT,SINDIR;
+		cmp	SINDEX, A jne copy_scb_tofifo_loop;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
 dma_scb_fromhost:
-	call	dma_finish;
-	/* If we were putting the SCB, we are done */
-	test	DMAPARAMS, DIRECTION	jz	return;
-	mvi	SCB_CONTROL  call dfdat_in_7;
-	call	dfdat_in_7_continued;
-	call	dfdat_in_7_continued;
-	jmp	dfdat_in_7_continued;
+		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;
+		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;
+		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
@@ -1188,13 +1415,13 @@
 	ret;
 
 add_scb_to_free_list:
-.if ( SCB_PAGING )
-	mov	SCB_NEXT, FREE_SCBH;
-	mov	FREE_SCBH, SCBPTR;
-.endif
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	SCB_NEXT, FREE_SCBH;
+		mov	FREE_SCBH, SCBPTR;
+	}
 	mvi	SCB_TAG, SCB_LIST_NULL ret;
 
-.if ( SCB_PAGING )
+if ((p->flags & AHC_PAGESCBS) != 0) {
 get_free_or_disc_scb:
 	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
 	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
@@ -1211,7 +1438,7 @@
 dequeue_free_scb:
 	mov	SCBPTR, FREE_SCBH;
 	mov	FREE_SCBH, SCB_NEXT ret;
-.endif
+}
 
 add_scb_to_disc_list:
 /*
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h
--- v2.0.35/linux/drivers/scsi/aic7xxx/sequencer.h	Mon Jul 13 13:46:34 1998
+++ linux/drivers/scsi/aic7xxx/sequencer.h	Sun Nov 15 10:33:07 1998
@@ -2,7 +2,7 @@
  * Instruction formats for the sequencer program downloaded to
  * Aic7xxx SCSI host adapters
  *
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -36,43 +36,81 @@
  *      $Id: sequencer.h,v 1.3 1997/09/27 19:37:31 gibbs Exp $
  */
 
+#ifdef __LITTLE_ENDIAN_BITFIELD
 struct ins_format1 {
-	unsigned char immediate;
-	unsigned char source;
-	unsigned char destination;
-	unsigned char opcode_ret;
-#define DOWNLOAD_CONST_IMMEDIATE 0x80
+	unsigned int
+			immediate	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
 };
 
 struct ins_format2 {
-	unsigned char shift_control;
-	unsigned char source;
-	unsigned char destination;
-	unsigned char opcode_ret;
-#define RETURN_BIT 0x01
+	unsigned int
+			shift_control	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
 };
 
 struct ins_format3 {
-	unsigned char immediate;
-	unsigned char source;
-	unsigned char address;
-	unsigned char opcode_addr;
-#define ADDR_HIGH_BIT 0x01
+	unsigned int
+			immediate	: 8,
+			source		: 9,
+			address		: 10,
+			opcode		: 4,
+			parity		: 1;
+};
+#elif defined(__BIG_ENDIAN_BITFIELD)
+struct ins_format1 {
+	unsigned int
+			parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			immediate	: 8;
 };
 
-#ifndef __KERNEL__
-struct instruction {
-	union {
+struct ins_format2 {
+	unsigned int
+			parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			shift_control	: 8;
+};
+
+struct ins_format3 {
+	unsigned int
+			parity		: 1,
+			opcode		: 4,
+			address		: 10,
+			source		: 9,
+			immediate	: 8;
+};
+#endif
+
+union ins_formats {
 		struct ins_format1 format1;
 		struct ins_format2 format2;
 		struct ins_format3 format3;
 		unsigned char	   bytes[4];
-	} format;
-	u_int	srcline;
+		unsigned int	   integer;
+};
+struct instruction {
+	union	ins_formats format;
+	unsigned int	srcline;
 	struct symbol *patch_label;
-	STAILQ_ENTRY(instruction) links;
+  struct {
+    struct instruction *stqe_next;
+  } links;
 };
-#endif
 
 #define	AIC_OP_OR	0x0
 #define	AIC_OP_AND	0x1
@@ -80,6 +118,7 @@
 #define	AIC_OP_ADD	0x3
 #define	AIC_OP_ADC	0x4
 #define	AIC_OP_ROL	0x5
+#define	AIC_OP_BMOV	0x6
 
 #define	AIC_OP_JMP	0x8
 #define AIC_OP_JC	0x9
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.0.35/linux/drivers/scsi/aic7xxx.c	Sun Nov 15 10:49:44 1998
+++ linux/drivers/scsi/aic7xxx.c	Sun Nov 15 10:33:08 1998
@@ -98,7 +98,7 @@
 
 /*+M**************************************************************************
  *
- * Further driver modifications made by Doug Ledford <dledford@dialnet.net>
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
  *
  * Copyright (c) 1997-1998 Doug Ledford
  *
@@ -163,13 +163,159 @@
  *
  *_M*************************************************************************/
 
-#ifdef MODULE
+/*
+ * The next three defines are user configurable.  These should be the only
+ * defines a user might need to get in here and change.  There are other
+ * defines buried deeper in the code, but those really shouldn't need touched
+ * under normal conditions.
+ */
+
+/*
+ * AIC7XXX_FAKE_NEGOTIATION_CMDS
+ *   We now have two distinctly different methods of device negotiation
+ *   in this code.  The two methods are selected by either defining or not
+ *   defining this option.  The difference is as follows:
+ *
+ *   With AIC7XXX_FAKE_NEGOTIATION_CMDS not set (commented out)
+ *     When the driver is in need of issuing a negotiation command for any
+ *     given device, it will add the negotiation message on to part of a
+ *     regular SCSI command for the device.  In the process, if the device
+ *     is configured for and using tagged queueing, then the code will
+ *     also issue that single command as a non-tagged command, attach the
+ *     negotiation message to that one command, and use a temporary
+ *     queue depth of one to keep the untagged and tagged commands from
+ *     overlapping.
+ *       Pros: This doesn't use any extra SCB structures, it's simple, it
+ *         works most of the time (if not all of the time now), and
+ *         since we get the device capability info frmo the INQUIRY data
+ *         now, shouldn't cause any problems.
+ *       Cons: When we need to send a negotiation command to a device, we
+ *         must use a command that is being sent to LUN 0 of the device.
+ *         If we try sending one to high LUN numbers, then some devices
+ *         get noticeably upset.  Since we have to wait for a command with
+ *         LUN == 0 to come along, we may not be able to renegotiate when
+ *         we want if the user is actually using say LUN 1 of a CD Changer
+ *         instead of using LUN 0 for an extended period of time.
+ *
+ *   With AIC7XXX_FAKE_NEGOTIATION_CMDS defined
+ *     When we need to negotiate with a device, instead of attaching our
+ *     negotiation message to an existing command, we insert our own
+ *     fictional Scsi_Cmnd into the chain that has the negotiation message
+ *     attached to it.  We send this one command as untagged regardless
+ *     of the device type, and we fiddle with the queue depth the same as
+ *     we would with the option unset to avoid overlapping commands.  The
+ *     primary difference between this and the unset option is that the
+ *     negotiation message is no longer attached to a specific command,
+ *     instead it is its own command and is merely triggered by a
+ *     combination of both A) We need to negotiate and B) The mid level
+ *     SCSI code has sent us a command.  We still don't do any negotiation
+ *     unless there is a valid SCSI command to be processed.
+ *       Pros: This fixes the problem above in the Cons section.  Since we
+ *         issue our own fake command, we can set the LUN to 0 regardless
+ *         of what the LUN is in the real command.  It also means that if
+ *         the device get's nasty over negotiation issues, it won't be
+ *         showing up on a regular command, so we won't get any SENSE buffer
+ *         data or STATUS_BYTE returns to the mid level code that are caused
+ *         by snits in the negotiation code.
+ *       Cons: We add more code, and more complexity.  This means more ways
+ *         in which things could break.  It means a larger driver.  It means
+ *         more resource consumption for the fake commands.  However, the
+ *         biggest problem is this.  Take a system where there is a CD-ROM
+ *         on the SCSI bus.  Someone has a CD in the CD-ROM and is using it.
+ *         For some reason the SCSI bus gets reset.  We don't touch the
+ *         CD-ROM again for quite a period of time (so we don't renegotiate
+ *         after the reset until we do touch the CD-ROM again).  In the
+ *         time while we aren't using the CD-ROM, the current disc is
+ *         removed and a new one put in.  When we go to check that disc, we
+ *         will first have to renegotiate.  In so doing, we issue our fake
+ *         SCSI command, which happens to be TEST_UNIT_READY.  The CD-ROM
+ *         negotiates with us, then responds to our fake command with a
+ *         CHECK_CONDITION status.  We REQUEST_SENSE from the CD-ROM, it
+ *         then sends the SENSE data to our fake command to tell it that
+ *         it has been through a disc change.  There, now we've cleared out
+ *         the SENSE data along with our negotiation command, and when the
+ *         real command executes, it won't pick up that the CD was changed.
+ *         That's the biggest Con to this approach.  In the future, I could
+ *         probably code around this problem though, so this option is still
+ *         viable.
+ *
+ *  So, which command style should you use?  I would appreciate it if people
+ *  could try out both types.  I want to know about any cases where one
+ *  method works and the other doesn't.  If one method works on significantly
+ *  more systems than another, then it will become the default.  If the second
+ *  option turns out to work best, then I'll find a way to work around that
+ *  big con I listed.
+ *
+ *  -- July 7, 02:33
+ *    OK...I just added some code that should make the Con listed for the
+ *    fake commands a non issue now.  However, it needs testing.  For now,
+ *    I'm going to make the default to use the fake commands, we'll see how
+ *    it goes.
+ */
+ 
+#define AIC7XXX_FAKE_NEGOTIATION_CMDS
+
+/*
+ * AIC7XXX_STRICT_PCI_SETUP
+ *   Should we assume the PCI config options on our controllers are set with
+ *   sane and proper values, or should we be anal about our PCI config
+ *   registers and force them to what we want?  The main advantage to
+ *   defining this option is on non-Intel hardware where the BIOS may not
+ *   have been run to set things up, or if you have one of the BIOSless
+ *   Adaptec controllers, such as a 2910, that don't get set up by the
+ *   BIOS.  However, keep in mind that we really do set the most important
+ *   items in the driver regardless of this setting, this only controls some
+ *   of the more esoteric PCI options on these cards.  In that sense, I
+ *   would default to leaving this off.  However, if people wish to try
+ *   things both ways, that would also help me to know if there are some
+ *   machines where it works one way but not another.
+ *
+ *   -- July 7, 17:09
+ *     OK...I need this on my machine for testing, so the default is to
+ *     leave it defined.
+ *
+ *   -- July 7, 18:49
+ *     I needed it for testing, but it didn't make any difference, so back
+ *     off she goes.
+ *
+ *   -- July 16, 23:04
+ *     I turned it back on to try and compensate for the 2.1.x PCI code
+ *     which no longer relies solely on the BIOS and now tries to set
+ *     things itself.
+ */
+
+#define AIC7XXX_STRICT_PCI_SETUP
+
+/*
+ * AIC7XXX_VERBOSE_DEBUGGING
+ *   This option enables a lot of extra printk();s in the code, surrounded
+ *   by if (aic7xxx_verbose ...) statements.  Executing all of those if
+ *   statements and the extra checks can get to where it actually does have
+ *   an impact on CPU usage and such, as well as code size.  Disabling this
+ *   define will keep some of those from becoming part of the code.
+ *
+ *   NOTE:  Currently, this option has no real effect, I will be adding the
+ *   various #ifdef's in the code later when I've decided a section is
+ *   complete and no longer needs debugging.  OK...a lot of things are now
+ *   surrounded by this define, so turning this off does have an impact.
+ */
+ 
+/*
+ * #define AIC7XXX_VERBOSE_DEBUGGING
+ */
+ 
+#if defined(MODULE) || defined(PCMCIA)
 #include <linux/module.h>
 #endif
 
+#if defined(PCMCIA)
+#  undef MODULE
+#endif
+
 #include <stdarg.h>
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/byteorder.h>
 #include <linux/version.h>
 #include <linux/string.h>
 #include <linux/errno.h>
@@ -190,7 +336,6 @@
 #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() */
@@ -209,7 +354,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.0.19"
+#define AIC7XXX_C_VERSION  "5.1.4"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -242,17 +387,15 @@
 #  include <linux/bios32.h>
 #endif
 
-#if defined(__powerpc__) || defined(__i386__)
-#  define MMAPIO
-#endif
-
 #if defined(__powerpc__)
+#  define MMAPIO
 #  ifdef mb
 #    undef mb
 #  endif
 #  define mb() \
      __asm__ __volatile__("eieio" ::: "memory")
 #elif defined(__i386__)
+#  define MMAPIO
 #  ifdef mb
 #    undef mb
 #  endif
@@ -330,7 +473,7 @@
  *       tagged queue value array is always active now.  I've added
  *       a setup option to set this particular array and I'm hoping
  *       insmod will be smart enough to set it properly as well.  It's
- *       by use of this array that a person can disable tagged queueing.
+ *       by use of this array that a person can enable tagged queueing.
  *       The DEFAULT_TAG_COMMANDS define has been changed to disable
  *       tagged queueing by default, so if your devices can handle tagged
  *       queueing you will need to add a line to their lilo.conf file like:
@@ -396,6 +539,9 @@
  */
 
 /*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is located around line
+ *       number 1305
 adapter_tag_info_t aic7xxx_tag_info[] =
 {
   {DEFAULT_TAG_COMMANDS},
@@ -429,7 +575,11 @@
   "Adaptec AHA-394X Ultra SCSI host adapter",           /* AIC_7882 */
   "Adaptec AHA-398X Ultra SCSI host adapter",           /* AIC_7883 */
   "Adaptec AHA-2944 Ultra SCSI host adapter",           /* AIC_7884 */
-  "Adaptec AIC-7895 Ultra SCSI host adapter"            /* AIC_7895 */
+  "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
+  "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
+  "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
+  "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
+  "Adaptec AHA-394X Ultra2 SCSI host adapter"           /* AIC_7897 */
 };
 
 /*
@@ -467,10 +617,10 @@
 /*
  * Standard EISA Host ID regs  (Offset from slot base)
  */
-#define HID0                0x80   /* 0,1: msb of ID2, 2-7: ID1      */
-#define HID1                0x81   /* 0-4: ID3, 5-7: LSB ID2         */
-#define HID2                0x82   /* product                        */
-#define HID3                0x83   /* firmware revision              */
+#define AHC_HID0              0x80   /* 0,1: msb of ID2, 2-7: ID1      */
+#define AHC_HID1              0x81   /* 0-4: ID3, 5-7: LSB ID2         */
+#define AHC_HID2              0x82   /* product                        */
+#define AHC_HID3              0x83   /* firmware revision              */
 
 /*
  * AIC-7770 I/O range to reserve for a card
@@ -494,11 +644,13 @@
 #define                LATTIME                0x0000FF00ul
 
 #define        DEVCONFIG                0x40
-#define                SCBSIZE32        0x00010400ul        /* aic789X only */
+#define                SCBSIZE32        0x00010000ul        /* aic789X only */
 #define                MPORTMODE        0x00000400ul        /* aic7870 only */
 #define                RAMPSM           0x00000200ul        /* aic7870 only */
+#define                RAMPSM_ULTRA2    0x00000004
 #define                VOLSENSE         0x00000100ul
 #define                SCBRAMSEL        0x00000080ul
+#define                SCBRAMSEL_ULTRA2 0x00000008
 #define                MRDCEN           0x00000040ul
 #define                EXTSCBTIME       0x00000020ul        /* aic7870 only */
 #define                EXTSCBPEN        0x00000010ul        /* aic7870 only */
@@ -507,6 +659,8 @@
 #define                STPWLEVEL        0x00000002ul
 #define                DIFACTNEGEN      0x00000001ul        /* aic7870 only */
 
+#define        SCAMCTL                  0x1a                /* Ultra2 only  */
+#define        CCSCBBADDR               0xf0                /* aic7895/6/7  */
 
 /*
  * Define the different types of SEEPROMs on aic7xxx adapters
@@ -534,12 +688,14 @@
 #define CFDISC                0x0010      /* enable disconnection */
 #define CFWIDEB               0x0020      /* wide bus device (wide card) */
 #define CFSYNCHISULTRA        0x0040      /* CFSYNC is an ultra offset */
-/* UNUSED                0x0080 */
+#define CFNEWULTRAFORMAT      0x0080      /* Use the Ultra2 SEEPROM format */
 #define CFSTART               0x0100      /* send start unit SCSI command */
 #define CFINCBIOS             0x0200      /* include in BIOS scan */
 #define CFRNFOUND             0x0400      /* report even if not found */
 #define CFMULTILUN            0x0800      /* probe mult luns in BIOS scan */
-/* UNUSED                0xF000 */
+#define CFWBCACHEYES          0x4000      /* Enable W-Behind Cache on drive */
+#define CFWBCACHENC           0xc000      /* Don't change W-Behind Cache */
+/* UNUSED                0x3000 */
   unsigned short device_flags[16];        /* words 0-15 */
 
 /*
@@ -569,7 +725,9 @@
 #define CF284XSTERM     0x0020  /* SCSI low byte termination (284x cards) */
 #define CFRESETB        0x0040  /* reset SCSI bus at boot */
 #define CFBPRIMARY      0x0100  /* Channel B primary on 7895 chipsets */
-/* UNUSED                0xFE80 */
+#define CFSEAUTOTERM    0x0400  /* aic7890 Perform SE Auto Term */
+#define CFLVDSTERM      0x0800  /* aic7890 LVD Termination */
+/* UNUSED                0xF280 */
   unsigned short adapter_control;        /* word 17 */
 
 /*
@@ -682,7 +840,6 @@
 
 typedef enum {
         SCB_FREE                = 0x0000,
-        SCB_WDTR_16BIT          = 0x0001,
         SCB_WAITINGQ            = 0x0002,
         SCB_ACTIVE              = 0x0004,
         SCB_SENSE               = 0x0008,
@@ -691,11 +848,12 @@
         SCB_RESET               = 0x0040,
         SCB_RECOVERY_SCB        = 0x0080,
         SCB_WAS_BUSY            = 0x0100,
+        SCB_MSGOUT_SENT         = 0x0200,
         SCB_MSGOUT_SDTR         = 0x0400,
         SCB_MSGOUT_WDTR         = 0x0800,
-        SCB_MSGOUT_WDTR_8BIT    = 0x0800,
-        SCB_MSGOUT_WDTR_16BIT   = 0x0801,
-        SCB_MSGOUT_BITS         = SCB_MSGOUT_SDTR | SCB_MSGOUT_WDTR_16BIT,
+        SCB_MSGOUT_BITS         = SCB_MSGOUT_SENT | 
+                                  SCB_MSGOUT_SDTR |
+                                  SCB_MSGOUT_WDTR,
         SCB_QUEUED_ABORT        = 0x1000,
         SCB_QUEUED_FOR_DONE     = 0x2000
 } scb_flag_type;
@@ -711,7 +869,9 @@
         AHC_EXTEND_TRANS_A        = 0x00000100,
         AHC_EXTEND_TRANS_B        = 0x00000200,
         AHC_TERM_ENB_A            = 0x00000400,
+        AHC_TERM_ENB_SE_LOW       = 0x00000400,
         AHC_TERM_ENB_B            = 0x00000800,
+        AHC_TERM_ENB_SE_HIGH      = 0x00000800,
         AHC_HANDLING_REQINITS     = 0x00001000,
         AHC_TARGETMODE            = 0x00002000,
         AHC_NEWEEPROM_FMT         = 0x00004000,
@@ -726,45 +886,62 @@
         AHC_B_SCANNED             = 0x00200000,
         AHC_MULTI_CHANNEL         = 0x00400000,
         AHC_BIOS_ENABLED          = 0x00800000,
-        AHC_ABORT_PENDING         = 0x02000000,
-        AHC_RESET_PENDING         = 0x04000000,
+        AHC_SEEPROM_FOUND         = 0x01000000,
+        AHC_TERM_ENB_LVD          = 0x02000000,
+        AHC_ABORT_PENDING         = 0x04000000,
+        AHC_RESET_PENDING         = 0x08000000,
 #define AHC_IN_ISR_BIT              28
         AHC_IN_ISR                = 0x10000000,
         AHC_IN_ABORT              = 0x20000000,
-        AHC_IN_RESET              = 0x40000000
+        AHC_IN_RESET              = 0x40000000,
+        AHC_EXTERNAL_SRAM         = 0x80000000
 } ahc_flag_type;
 
 typedef enum {
-        AHC_NONE                  = 0x00000000,
-        AHC_ULTRA                 = 0x00000001,
-        AHC_WIDE                  = 0x00000002,
-        AHC_TWIN                  = 0x00000008,
-        AHC_AIC7770               = 0x00000010,
-        AHC_AIC7850               = 0x00000020,
-        AHC_AIC7860               = 0x00000021,
-        AHC_AIC7870               = 0x00000040,
-        AHC_AIC7880               = 0x00000041,
-        AHC_AIC7895               = 0x00000081,
-        AHC_AIC78x0               = 0x000000E0,
-        AHC_274                   = 0x00000110,
-        AHC_284                   = 0x00000210,
-        AHC_294AU                 = 0x00000421,
-        AHC_294                   = 0x00000440,
-        AHC_294U                  = 0x00000441,
-        AHC_394                   = 0x00000840,
-        AHC_394U                  = 0x00000841,
-        AHC_394AU                 = 0x00000881,
-        AHC_398                   = 0x00001040,
-        AHC_398U                  = 0x00001041,
-        AHC_39x                   = 0x00001880
-} ahc_type;
+  AHC_NONE             = 0x0000,
+  AHC_CHIPID_MASK      = 0x00ff,
+  AHC_AIC7770          = 0x0001,
+  AHC_AIC7850          = 0x0002,
+  AHC_AIC7860          = 0x0003,
+  AHC_AIC7870          = 0x0004,
+  AHC_AIC7880          = 0x0005,
+  AHC_AIC7890          = 0x0006,
+  AHC_AIC7895          = 0x0007,
+  AHC_AIC7896          = 0x0008,
+  AHC_VL               = 0x0100,
+  AHC_EISA             = 0x0200,
+  AHC_PCI              = 0x0400,
+} ahc_chip;
+
+typedef enum {
+  AHC_FENONE           = 0x0000,
+  AHC_ULTRA            = 0x0001,
+  AHC_ULTRA2           = 0x0002,
+  AHC_WIDE             = 0x0004,
+  AHC_TWIN             = 0x0008,
+  AHC_MORE_SRAM        = 0x0010,
+  AHC_CMD_CHAN         = 0x0020,
+  AHC_QUEUE_REGS       = 0x0040,
+  AHC_SG_PRELOAD       = 0x0080,
+  AHC_SPIOCAP          = 0x0100,
+  AHC_AIC7770_FE       = AHC_FENONE,
+  AHC_AIC7850_FE       = AHC_SPIOCAP,
+  AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
+  AHC_AIC7870_FE       = AHC_FENONE,
+  AHC_AIC7880_FE       = AHC_ULTRA,
+  AHC_AIC7890_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|
+                         AHC_QUEUE_REGS|AHC_SG_PRELOAD,
+  AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
+  AHC_AIC7896_FE       = AHC_AIC7890_FE,
+} ahc_feature;
 
 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 */
+        volatile scb_flag_type flags;         /* current state of scb */
         struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        void                  *kmalloc_ptr;
         unsigned char          tag_action;
         unsigned char          sg_count;
         unsigned char          sense_cmd[6];  /*
@@ -792,28 +969,50 @@
   { ILLHADDR,  "Illegal Host Access" },
   { ILLSADDR,  "Illegal Sequencer Address referenced" },
   { ILLOPCODE, "Illegal Opcode in sequencer program" },
-  { SQPARERR,  "Sequencer Ram Parity Error" }
+  { SQPARERR,  "Sequencer Ram Parity Error" },
+  { DPARERR,   "Data-Path Ram Parity Error" },
+  { MPARERR,   "Scratch Ram/SCB Array Ram Parity Error" },
+  { PCIERRSTAT,"PCI Error detected" },
+  { CIOPARERR, "CIOBUS Parity Error" }
 };
 
 static unsigned char
 generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
 
 typedef struct {
-  struct aic7xxx_hwscb *hscbs;
   scb_queue_type free_scbs;        /*
                                     * SCBs assigned to free slot on
                                     * card (no paging required)
                                     */
+  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  struct aic7xxx_hwscb *hscbs;
   unsigned char  numscbs;          /* current number of scbs */
   unsigned char  maxhscbs;         /* hardware scbs */
   unsigned char  maxscbs;          /* max scbs including pageable scbs */
-  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  void          *hscb_kmalloc_ptr;
 } scb_data_type;
 
+struct target_cmd {
+  unsigned char mesg_bytes[4];
+  unsigned char command[28];
+};
+
+#define AHC_TRANS_CUR    0x0001
+#define AHC_TRANS_ACTIVE 0x0002
+#define AHC_TRANS_GOAL   0x0004
+#define AHC_TRANS_USER   0x0008
+#define AHC_TRANS_QUITE  0x0010
 typedef struct {
-  unsigned char period;
-  unsigned char offset;
-} syncinfo_type;
+  unsigned char cur_width;
+  unsigned char goal_width;
+  unsigned char cur_period;
+  unsigned char goal_period;
+  unsigned char cur_offset;
+  unsigned char goal_offset;
+  unsigned char user_width;
+  unsigned char user_period;
+  unsigned char user_offset;
+} transinfo_type;
 
 /*
  * Define a structure used for each host adapter.  Note, in order to avoid
@@ -841,15 +1040,18 @@
   spinlock_t               spin_lock;
 #endif
   volatile unsigned char   cpu_lock_count[NR_CPUS];
-  ahc_type                 type;             /* card type */
+  ahc_chip                 chip;             /* chip type */
+  ahc_feature              features;         /* chip features */
   unsigned long            last_reset;
   unsigned long            isr_count;        /* Interrupt count */
   unsigned long            spurious_int;
+  struct target_cmd       *targetcmds;
+  unsigned int             num_targetcmds;
   unsigned short           discenable;       /* Targets allowed to disconnect */
   unsigned short           tagenable;        /* Targets using tagged I/O */
   unsigned short           orderedtag;       /* Ordered Q tags allowed */
   volatile unsigned char   activescbs;       /* active scbs */
-  unsigned char            max_activescbs;
+  volatile unsigned char   max_activescbs;
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
   volatile unsigned char   qoutfifonext;
@@ -870,7 +1072,7 @@
 #define  DEVICE_SCANNED                 0x80
   volatile unsigned char   dev_flags[MAX_TARGETS];
   volatile unsigned char   dev_active_cmds[MAX_TARGETS];
-  unsigned char            dev_temp_queue_depth[MAX_TARGETS];
+  volatile unsigned char   dev_temp_queue_depth[MAX_TARGETS];
   unsigned char            dev_commands_sent[MAX_TARGETS];
 
   /*
@@ -917,7 +1119,7 @@
 #define MSG_TYPE_INITIATOR_MSGIN   0x02
   unsigned char            msg_len;          /* Length of message */
   unsigned char            msg_index;        /* Index into msg_buf array */
-  syncinfo_type            syncinfo[MAX_TARGETS];
+  transinfo_type           transinfo[MAX_TARGETS];
   volatile scb_queue_type  waiting_scbs;     /*
                                               * SCBs waiting for space in
                                               * the QINFIFO.
@@ -945,10 +1147,10 @@
   volatile unsigned char   qoutfifo[256];
   volatile unsigned char   qinfifo[256];
   unsigned int             irq;              /* IRQ for this adapter */
-  unsigned short           needsdtr;
-  unsigned short           sdtr_pending;
-  unsigned short           needwdtr;
-  unsigned short           wdtr_pending;
+  volatile unsigned short  needsdtr;
+  volatile unsigned short  sdtr_pending;
+  volatile unsigned short  needwdtr;
+  volatile unsigned short  wdtr_pending;
   int                      instance;         /* aic7xxx instance number */
   int                      scsi_id;          /* host adapter SCSI ID */
   int                      scsi_id_b;        /* channel B for twin adapters */
@@ -965,8 +1167,10 @@
 #endif
   unsigned char            pci_bus;
   unsigned char            pci_device_fn;
+  struct seeprom_config    sc;
+  unsigned short           sc_type;
+  unsigned short           sc_size;
 
-#ifdef AIC7XXX_PROC_STATS
   /*
    * Statistics Kept:
    *
@@ -980,18 +1184,21 @@
    *
    * NOTE: Enabling this feature is likely to cause a noticeable performance
    * decrease as the accesses into the stats structures blows apart multiple
-   * cache lines and is CPU time consuming.
+   * cache lines and is CPU time consuming.  We keep the xfer count always
+   * for use by the aic7xxx_proc.c code, but only do the bins if the
+   * proc stats code is enabled.
    */
   struct aic7xxx_xferstats {
     long xfers;                              /* total xfer count */
     long w_total;                            /* total writes */
     long w_total512;                         /* 512 byte blocks written */
-    long w_bins[10];                         /* binned write */
     long r_total;                            /* total reads */
     long r_total512;                         /* 512 byte blocks read */
+#ifdef AIC7XXX_PROC_STATS
+    long w_bins[10];                         /* binned write */
     long r_bins[10];                         /* binned reads */
-  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
 #endif /* AIC7XXX_PROC_STATS */
+  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
 };
 
 /*
@@ -999,29 +1206,33 @@
  * 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;
+#define AHC_SYNCRATE_ULTRA2 0
+#define AHC_SYNCRATE_ULTRA  2
+#define AHC_SYNCRATE_FAST   5
+static struct aic7xxx_syncrate {
   /* Rates in Ultra mode have bit 8 of sxfr set */
 #define                ULTRA_SXFR 0x100
-  short rate;
-  const char *english;
+  int sxfr_ultra2;
+  int sxfr;
+  unsigned char period;
+  const char *rate[2];
 } aic7xxx_syncrates[] = {
-  { 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"  }
+  { 0x13,  0x000,  10,  {"40.0", "80.0"} },
+  { 0x14,  0x000,  11,  {"33.0", "66.6"} },
+  { 0x15,  0x100,  12,  {"20.0", "40.0"} },
+  { 0x16,  0x110,  15,  {"16.0", "32.0"} },
+  { 0x17,  0x120,  18,  {"13.4", "26.8"} },
+  { 0x18,  0x000,  25,  {"10.0", "20.0"} },
+  { 0x19,  0x010,  31,  {"8.0",  "16.0"} },
+  { 0x1a,  0x020,  37,  {"6.67", "13.3"} },
+  { 0x1b,  0x030,  43,  {"5.7",  "11.4"} },
+  { 0x10,  0x040,  50,  {"5.0",  "10.0"} },
+  { 0x00,  0x050,  56,  {"4.4",  "8.8" } },
+  { 0x00,  0x060,  62,  {"4.0",  "8.0" } },
+  { 0x00,  0x070,  68,  {"3.6",  "7.2" } },
+  { 0x00,  0x000,  0,   {NULL, NULL}   },
 };
 
-static int num_aic7xxx_syncrates =
-    sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);
-
 #define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1),  \
                         (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
                         ((scb->hscb)->target_channel_lun & 0x07)
@@ -1041,58 +1252,170 @@
 
 /*
  * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *       cards in the system.  This should be fixed.
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
  */
-static int aic7xxx_7895_irq_hack = -1;       /* This enables a hack to fix
-                                              * IRQ settings on buggy 7895
-                                              * MB controller setups
-                                              *  -1 == Disable this hack
-                                              *   0 == Use the Channel A IRQ
-                                              *   1 == Use the Channel B IRQ
-                                              */
-static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
-static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
-static int aic7xxx_irq_trigger = -1;         /*
-                                              * -1 use board setting
-                                              *  0 use edge triggered
-                                              *  1 use level triggered
-                                              */
-static int aic7xxx_reverse_scan = 0;         /*
-                                              * Set this to anything but 0
-                                              * to make the probe code
-                                              * reverse the order of PCI
-                                              * devices
-                                              */
-static int aic7xxx_override_term = 0;        /*
-                                              * Set this to non-0 to make the
-                                              * driver override any BIOS
-                                              * configured termination
-                                              * settings based upon the
-                                              * results of the cable detect
-                                              * logic.  This only applies
-                                              * to cards that have cable
-                                              * detection logic and a SEEPROM
-                                              */
-static int aic7xxx_panic_on_abort = 0;       /*
-                                              * Set this to non-0 in order
-                                              * to force the driver to panic
-                                              * the kernel and print out
-                                              * debugging info on an abort
-                                              * or reset call into the
-                                              * driver.
-                                              */
-static int aic7xxx_pci_parity = 0;           /*
-                                              * Set this to:
-                                              *   0 - Shut off PCI parity check
-                                              *  -1 - Normal parity check
-                                              *  anything else - reverse pci
-                                              *    pci parity checking
-                                              */
+
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static unsigned int aic7xxx_no_reset = 0;
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips.  The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect.  Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from.  The only exceptions to this are when a controller
+ * has its BIOS disabled.  So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number.  We also force
+ * all controllers with their BIOS disabled to the end of the list.  This
+ * works on *almost* all computers.  Where it doesn't work, we have this
+ * option.  Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end.  That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+/*
+ * This setting enables a hack to fix the IRQ settings on buggy 7895
+ * MB controller setups:
+ *    -1 == Disable this hack
+ *     0 == Use the Channel A IRQ for both channels
+ *     1 == Use the Channel B IRQ for both channels
+ */
+static unsigned int aic7xxx_extended = 0;
+/*
+ * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
+ *   -1 = Use detected settings.
+ *    0 = Force Edge triggered mode.
+ *    1 = Force Level triggered mode.
+ */
+static int aic7xxx_irq_trigger = -1;
+/*
+ * This variable is used to override the termination settings on a controller.
+ * This should not be used under normal conditions.  However, in the case
+ * that a controller does not have a readable SEEPROM (so that we can't
+ * read the SEEPROM settings directly) and that a controller has a buggered
+ * version of the cable detection logic, this can be used to force the 
+ * correct termination.  It is preferable to use the manual termination
+ * settings in the BIOS if possible, but some motherboard controllers store
+ * those settings in a format we can't read.  In other cases, auto term
+ * should also work, but the chipset was put together with no auto term
+ * logic (common on motherboard controllers).  In those cases, we have
+ * 32 bits here to work with.  That's good for 8 controllers/channels.  The
+ * bits are organized as 4 bits per channel, with scsi0 getting the lowest
+ * 4 bits in the int.  A 1 in a bit position indicates the termination setting
+ * that corresponds to that bit should be enabled, a 0 is disabled.
+ * It looks something like this:
+ *
+ *    0x0f =  1111-Single Ended Low Byte Termination on/off
+ *            ||\-Single Ended High Byte Termination on/off
+ *            |\-LVD Low Byte Termination on/off
+ *            \-LVD High Byte Termination on/off
+ *
+ * For non-Ultra2 controllers, the upper 2 bits are not important.  So, to
+ * enable both high byte and low byte termination on scsi0, I would need to
+ * make sure that the override_term variable was set to 0x03 (bits 0011).
+ * To make sure that all termination is enabled on an Ultra2 controller at
+ * scsi2 and only high byte termination on scsi1 and high and low byte
+ * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
+ *
+ * For the most part, users should never have to use this, that's why I
+ * left it fairly cryptic instead of easy to understand.  If you need it,
+ * most likely someone will be telling you what your's needs to be set to.
+ */
+static int aic7xxx_override_term = -1;
+/*
+ * Certain motherboard chipset controllers tend to screw
+ * up the polarity of the term enable output pin.  Use this variable
+ * to force the correct polarity for your system.  This is a bitfield variable
+ * similar to the previous one, but this one has one bit per channel instead
+ * of four.
+ *    0 = Force the setting to active low.
+ *    1 = Force setting to active high.
+ * Most Adaptec cards are active high, several motherboards are active low.
+ * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
+ * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
+ * to active high, you would need to set stpwlev=0x9 (bits 1001).
+ *
+ * People shouldn't need to use this, but if you are experiencing lots of
+ * SCSI timeout problems, this may help.  There is one sure way to test what
+ * this option needs to be.  Using a boot floppy to boot the system, configure
+ * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
+ * if needed then also pass a value to override_term to make sure that the
+ * driver is enabling SCSI termination, then set this variable to either 0
+ * or 1.  When the driver boots, make sure there are *NO* SCSI cables
+ * connected to your controller.  If it finds and inits the controller
+ * without problem, then the setting you passed to stpwlev was correct.  If
+ * the driver goes into a reset loop and hangs the system, then you need the
+ * other setting for this variable.  If neither setting lets the machine
+ * boot then you have definite termination problems that may not be fixable.
+ */
+static int aic7xxx_stpwlev = -1;
+/*
+ * Set this to non-0 in order to force the driver to panic the kernel
+ * and print out debugging info on a SCSI abort or reset cycle.
+ */
+static int aic7xxx_panic_on_abort = 0;
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ *   0 = Shut off PCI parity check
+ *  -1 = Normal polarity pci parity checking
+ *   1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number.  That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+/*
+ * Set this to any non-0 value to cause us to dump the contents of all
+ * the card's registers in a hex dump format tailored to each model of
+ * controller.
+ * 
+ * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION.
+ *       YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES
+ *       ONLY
+ */
+static int aic7xxx_dump_card = 0;
+/*
+ * Set this to a non-0 value to make us dump out the 32 bit instruction
+ * registers on the card after completing the sequencer download.  This
+ * allows the actual sequencer download to be verified.  It is possible
+ * to use this option and still boot up and run your system.  This is
+ * only intended for debugging purposes.
+ */
+static int aic7xxx_dump_sequencer = 0;
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices.  Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers.  NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+static int aic7xxx_no_probe = 0;
+
 /*
  * So that insmod can find the variable and make it point to something
  */
 #ifdef MODULE
 static char * aic7xxx = NULL;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,18)
+MODULE_PARM(aic7xxx, "s");
+#endif
 
 /*
  * Just in case someone uses commas to separate items on the insmod
@@ -1138,9 +1461,9 @@
 #define VERBOSE_SCSIINT        0x0004
 #define VERBOSE_PROBE          0x0008
 #define VERBOSE_PROBE2         0x0010
-#define VERBOSE_QUEUE          0x0020
+#define VERBOSE_NEGOTIATION2   0x0020
 #define VERBOSE_MINOR_ERROR    0x0040
-#define VERBOSE_QUEUE_FULL     0x0080
+#define VERBOSE_TRACING        0x0080
 #define VERBOSE_ABORT          0x0f00
 #define VERBOSE_ABORT_MID      0x0100
 #define VERBOSE_ABORT_FIND     0x0200
@@ -1157,49 +1480,75 @@
 
 /****************************************************************************
  *
- * These functions are not used yet, but when we do memory mapped
- * IO, we'll use them then.
+ * We're going to start putting in function declarations so that order of
+ * functions is no longer important.  As needed, they are added here.
+ *
+ ***************************************************************************/
+
+static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd);
+static void aic7xxx_print_card(struct aic7xxx_host *p);
+static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p);
+static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer);
+#endif
+
+/****************************************************************************
+ *
+ * These functions are now used.  They happen to be wrapped in useless
+ * inb/outb port read/writes around the real reads and writes because it
+ * seems that certain very fast CPUs have a problem dealing with us when
+ * going at full speed.
  *
  ***************************************************************************/
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+static inline void
+mdelay(int milliseconds)
+{
+  int i;
+
+  for(i=0; i<milliseconds; i++)
+    udelay(1000);
+}
+#endif
 
 static inline unsigned char
 aic_inb(struct aic7xxx_host *p, long port)
 {
+#ifdef MMAPIO
   unsigned char x;
   if(p->maddr)
+  {
     x = p->maddr[port];
+  }
   else
+  {
     x = inb(p->base + port);
+  }
   mb();
   return(x);
+#else
+  return(inb(p->base + port));
+#endif
 }
 
 static inline void
 aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
 {
+#ifdef MMAPIO
   if(p->maddr)
+  {
     p->maddr[port] = val;
+  }
   else
-    outb(val, p->base + port);
-  mb();
-}
-
-static inline void
-aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
-{
-  if(p->maddr)
   {
-    int i;
-
-    for (i=0; i < size; i++)
-    {
-      p->maddr[port] = valp[i];
-    }
+    outb(val, p->base + port);
   }
-  else
-    outsb(p->base + port, valp, size);
   mb();
+#else
+  outb(val, p->base + port);
+#endif
 }
 
 /*+F*************************************************************************
@@ -1227,10 +1576,13 @@
     { "irq_trigger", &aic7xxx_irq_trigger },
     { "verbose",     &aic7xxx_verbose },
     { "reverse_scan",&aic7xxx_reverse_scan },
-    { "7895_irq_hack", &aic7xxx_7895_irq_hack },
     { "override_term", &aic7xxx_override_term },
+    { "stpwlev", &aic7xxx_stpwlev },
+    { "no_probe", &aic7xxx_no_probe },
     { "panic_on_abort", &aic7xxx_panic_on_abort },
     { "pci_parity", &aic7xxx_pci_parity },
+    { "dump_card", &aic7xxx_dump_card },
+    { "dump_sequencer", &aic7xxx_dump_sequencer },
     { "tag_info",    NULL }
   };
 
@@ -1327,7 +1679,7 @@
         }
         else if (!strncmp(p, "verbose", n))
         {
-          *(options[i].flag) = 0xff69;
+          *(options[i].flag) = 0xff09;
         }
         else
         {
@@ -1387,54 +1739,66 @@
 static inline void
 restart_sequencer(struct aic7xxx_host *p)
 {
-  /* Set the sequencer address to 0. */
   aic_outb(p, 0, SEQADDR0);
   aic_outb(p, 0, SEQADDR1);
-
-  /*
-   * Reset and unpause the sequencer.  The reset is suppose to
-   * start the sequencer running, so we immediately do a pause_sequencer
-   * since some of our code expects the sequencer paused after a restart
-   */
-  aic_outb(p, SEQRESET | FASTMODE, SEQCTL);
-  pause_sequencer(p);
+  aic_outb(p, FASTMODE, SEQCTL);
 }
 
+/*
+ * We include the aic7xxx_seq.c file here so that the other defines have
+ * already been made, and so that it comes before the code that actually
+ * downloads the instructions (since we don't typically use function
+ * prototype, our code has to be ordered that way, it's a left-over from
+ * the original driver days.....I should fix it some time DL).
+ */
+#include "aic7xxx_seq.c"
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_next_patch
+ *   aic7xxx_check_patch
  *
  * Description:
- *   Find the next patch to download.
+ *   See if the next patch to download should be downloaded.
  *-F*************************************************************************/
-static struct sequencer_patch *
-aic7xxx_next_patch(struct sequencer_patch *cur_patch, int options, int instrptr)
+static int
+aic7xxx_check_patch(struct aic7xxx_host *p,
+  struct sequencer_patch **start_patch, int start_instr, int *skip_addr)
 {
-  while (cur_patch != NULL)
+  struct sequencer_patch *cur_patch;
+  struct sequencer_patch *last_patch;
+  int num_patches;
+
+  num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch);
+  last_patch = &sequencer_patches[num_patches];
+  cur_patch = *start_patch;
+
+  while ((cur_patch < last_patch) && (start_instr == cur_patch->begin))
   {
-    if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
-      || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
-      || (instrptr >= cur_patch->end))
+    if (cur_patch->patch_func(p) == 0)
     {
       /*
-       * Either we want to keep this section of code, or we have consumed
-       * this patch.  Skip to the next patch.
+       * Start rejecting code.
        */
-      cur_patch++;
-      if (cur_patch->options == 0)
-      {
-        /* Out of patches. */
-        cur_patch = NULL;
-      }
+      *skip_addr = start_instr + cur_patch->skip_instr;
+      cur_patch += cur_patch->skip_patch;
     }
     else
     {
-      /* Found an OK patch. */
-      break;
+      /*
+       * Found an OK patch.  Advance the patch pointer to the next patch
+       * and wait for our instruction pointer to get here.
+       */
+      cur_patch++;
     }
   }
-  return (cur_patch);
+
+  *start_patch = cur_patch;
+  if (start_instr < *skip_addr)
+    /*
+     * Still skipping
+     */
+    return (0);
+  return(1);
 }
 
 
@@ -1446,15 +1810,23 @@
  *   Find the next patch to download.
  *-F*************************************************************************/
 static void
-aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
+aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
+  unsigned char *dconsts)
 {
+  union ins_formats instr;
+  struct ins_format1 *fmt1_ins;
+  struct ins_format3 *fmt3_ins;
   unsigned char opcode;
-  struct ins_format3 instr;
-  unsigned char dconsts[4] = { 0, 0, 0, 0 };
 
-  instr = *(struct ins_format3 *) &seqprog[instrptr * 4];
+  instr = *(union ins_formats*) &seqprog[instrptr * 4];
+
+  instr.integer = le32_to_cpu(instr.integer);
+  
+  fmt1_ins = &instr.format1;
+  fmt3_ins = NULL;
+
   /* Pull the opcode */
-  opcode = (instr.opcode_addr & ~DOWNLOAD_CONST_IMMEDIATE) >> 1;
+  opcode = instr.format1.opcode;
   switch (opcode)
   {
     case AIC_OP_JMP:
@@ -1466,44 +1838,89 @@
     case AIC_OP_JE:
     case AIC_OP_JZ:
     {
+      struct sequencer_patch *cur_patch;
       int address_offset;
       unsigned int address;
-      struct sequencer_patch *patch;
+      int skip_addr;
       int i;
 
+      fmt3_ins = &instr.format3;
       address_offset = 0;
-      address = instr.address;
-      address |= (instr.opcode_addr & ADDR_HIGH_BIT) << 8;
-      for (i = 0; i < NUMBER(sequencer_patches); i++)
+      address = fmt3_ins->address;
+      cur_patch = sequencer_patches;
+      skip_addr = 0;
+
+      for (i = 0; i < address;)
       {
-        patch = &sequencer_patches[i];
-        if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
-            (((patch->options & options) != 0) && (patch->negative == TRUE)))
+        aic7xxx_check_patch(p, &cur_patch, i, &skip_addr);
+        if (skip_addr > i)
         {
-          if (address >= patch->end)
-          {
-            address_offset += patch->end - patch->begin;
-          }
+          int end_addr;
+
+          end_addr = MIN(address, skip_addr);
+          address_offset += end_addr - i;
+          i = skip_addr;
+        }
+        else
+        {
+          i++;
         }
       }
       address -= address_offset;
-      instr.address = address & 0xFF;
-      instr.opcode_addr &= ~ADDR_HIGH_BIT;
-      instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+      fmt3_ins->address = address;
+      /* Fall Through to the next code section */
     }
-       /*  Fall through  */
     case AIC_OP_OR:
     case AIC_OP_AND:
     case AIC_OP_XOR:
     case AIC_OP_ADD:
     case AIC_OP_ADC:
-      if (instr.opcode_addr & DOWNLOAD_CONST_IMMEDIATE)
+    case AIC_OP_BMOV:
+      if (fmt1_ins->parity != 0)
       {
-        instr.immediate = dconsts[instr.immediate];
+        fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
       }
-      instr.opcode_addr &= ~DOWNLOAD_CONST_IMMEDIATE;
+      fmt1_ins->parity = 0;
+      /* Fall Through to the next code section */
     case AIC_OP_ROL:
-      aic_outsb(p, SEQRAM, &instr.immediate, 4);
+      if ((p->features & AHC_ULTRA2) != 0)
+      {
+        int i, count;
+
+        /* Calculate odd parity for the instruction */
+        for ( i=0, count=0; i < 31; i++)
+        {
+          unsigned int mask;
+
+          mask = 0x01 << i;
+          if ((instr.integer & mask) != 0)
+            count++;
+        }
+        if (!(count & 0x01))
+          instr.format1.parity = 1;
+      }
+      else
+      {
+        if (fmt3_ins != NULL)
+        {
+          instr.integer =  fmt3_ins->immediate |
+                          (fmt3_ins->source << 8) |
+                          (fmt3_ins->address << 16) |
+                          (fmt3_ins->opcode << 25);
+        }
+        else
+        {
+          instr.integer =  fmt1_ins->immediate |
+                          (fmt1_ins->source << 8) |
+                          (fmt1_ins->destination << 16) |
+                          (fmt1_ins->ret << 24) |
+                          (fmt1_ins->opcode << 25);
+        }
+      }
+      aic_outb(p, (instr.integer & 0xff), SEQRAM);
+      aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
+      aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
+      aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
       break;
 
     default:
@@ -1523,89 +1940,93 @@
 static void
 aic7xxx_loadseq(struct aic7xxx_host *p)
 {
-  int options;
   struct sequencer_patch *cur_patch;
   int i;
   int downloaded;
+  int skip_addr;
+  unsigned char download_consts[4] = {0, 0, 0, 0};
 
   if (aic7xxx_verbose & VERBOSE_PROBE)
   {
     printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
   }
-  if (aic7xxx_verbose & VERBOSE_PROBE2)
-    printk("\n");
-  options = 1;  /* Code for all options. */
+  download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
+  cur_patch = &sequencer_patches[0];
   downloaded = 0;
-  if (p->type & AHC_ULTRA)
-  {
-    options |= ULTRA;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option ULTRA\n",
-        p->host_no);
-  }
-  if (p->type & AHC_TWIN)
-  {
-    options |= TWIN_CHANNEL;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option "
-        "TWIN_CHANNEL\n", p->host_no);
-  }
-  if (p->type & AHC_WIDE)
-  {
-    options |= WIDE;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option WIDE\n",
-        p->host_no);
-  }
-  /* if (p->scb_data->maxscbs > p->scb_data->maxhscbs)  this should always
-                                                        be true, don't test,
-                                                        just do.            */
-  {
-    options |= SCB_PAGING;
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(KERN_INFO "(scsi%d)   Will download code for option SCB_PAGING\n",
-        p->host_no);
-  }
-  /* We don't actually support target mode yet, so leave this out 
-  if (p->flags & AHC_TARGETMODE)
-    options |= TARGET_MODE; */
-
-  if ( (options & ~(ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01)) )
-  {
-    printk(KERN_INFO "(scsi%d) Unknown bits set in the options field, "
-      "correcting.\n", p->host_no);
-    options &= ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01;
-  }
+  skip_addr = 0;
 
-
-  cur_patch = sequencer_patches;
-  aic_outb(p, PERRORDIS | LOADRAM, SEQCTL);
+  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
   aic_outb(p, 0, SEQADDR0);
   aic_outb(p, 0, SEQADDR1);
 
   for (i = 0; i < sizeof(seqprog) / 4;  i++)
   {
-    cur_patch = aic7xxx_next_patch(cur_patch, options, i);
-    if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+    if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0)
     {
       /* Skip this instruction for this configuration. */
       continue;
     }
-    aic7xxx_download_instr(p, options, i);
+    aic7xxx_download_instr(p, i, &download_consts[0]);
     downloaded++;
   }
 
-  aic_outb(p, FASTMODE, SEQCTL);
   aic_outb(p, 0, SEQADDR0);
   aic_outb(p, 0, SEQADDR1);
-
-  if (aic7xxx_verbose & VERBOSE_PROBE2)
-    printk(KERN_INFO "(scsi%d) Download complete,", p->host_no);
-
+  aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+  unpause_sequencer(p, TRUE);
+  mdelay(1);
+  pause_sequencer(p);
+  aic_outb(p, FASTMODE, SEQCTL);
   if (aic7xxx_verbose & VERBOSE_PROBE)
   {
     printk(" %d instructions downloaded\n", downloaded);
   }
+  if (aic7xxx_dump_sequencer)
+    aic7xxx_print_sequencer(p, downloaded);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_sequencer
+ *
+ * Description:
+ *   Print the contents of the sequencer memory to the screen.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded)
+{
+  int i, k, temp;
+  
+  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+
+  k = 0;
+  for (i=0; i < downloaded; i++)
+  {
+    if ( k == 0 )
+      printk("%03x: ", i);
+    temp = aic_inb(p, SEQRAM);
+    temp |= (aic_inb(p, SEQRAM) << 8);
+    temp |= (aic_inb(p, SEQRAM) << 16);
+    temp |= (aic_inb(p, SEQRAM) << 24);
+    printk("%08x", temp);
+    if ( ++k == 8 )
+    {
+      printk("\n");
+      k = 0;
+    }
+    else
+      printk(" ");
+  }
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+  aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+  unpause_sequencer(p, TRUE);
+  mdelay(1);
+  pause_sequencer(p);
+  aic_outb(p, FASTMODE, SEQCTL);
+  printk("\n");
 }
 
 /*+F*************************************************************************
@@ -1613,23 +2034,14 @@
  *   aic7xxx_delay
  *
  * Description:
- *   Delay for specified amount of time.  We use udelay because the timer
+ *   Delay for specified amount of time.  We use mdelay 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)
 {
-  unsigned 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. */
-  }
+  mdelay(seconds * 1000);
 }
 
 /*+F*************************************************************************
@@ -1661,149 +2073,324 @@
   return(bp);
 }
 
-
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_scsirate
+ *   aic7xxx_find_syncrate
  *
  * Description:
  *   Look up the valid period to SCSIRATE conversion in our table
  *-F*************************************************************************/
-static unsigned char
-aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
-    unsigned char *period, unsigned char *offset, int target, int channel,
-    int set)
+static struct aic7xxx_syncrate *
+aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
+  unsigned int maxsync)
 {
-  int i = num_aic7xxx_syncrates;
-  unsigned char response_period;
-  unsigned char tindex;
-  unsigned short target_mask;
-  unsigned char lun;
-
-  tindex = target | (channel << 3);
-  target_mask = 0x01 << tindex;
-  lun = aic_inb(p, SCB_TCL) & 0x07;
-
-  response_period = *period;
+  struct aic7xxx_syncrate *syncrate;
 
-  /*
-   * If the offset is 0, then the device is requesting asynchronous
-   * transfers.
-   */
-  if ((*period != 0) && (*offset != 0))
+  syncrate = &aic7xxx_syncrates[maxsync];
+  while ( (syncrate->rate[0] != NULL) &&
+         (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
   {
-    for (i = 0; i < num_aic7xxx_syncrates; i++)
+    if ( *period <= syncrate->period )
     {
-      if (*period <= aic7xxx_syncrates[i].period)
+      /*
+       * When responding to a target that requests sync, the requested rate
+       * may fall between two rates that we can output, but still be a rate
+       * that we can receive.  Because of this, we want to respond with the
+       * same rate that it sent to us even if the persiod we use to send
+       * data to it is lower.  Only lower the response period if we must.
+       */
+      if(syncrate == &aic7xxx_syncrates[maxsync])
       {
-        /*
-         * Watch out for Ultra speeds when ultra is not enabled and
-         * vice-versa.
-         */
-        if (!(p->type & AHC_ULTRA) &&
-            (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
-        {
-          /*
-           * This should only happen if the drive is the first to negotiate
-           * and chooses a high rate.   We'll just move down the table until
-           * we hit a non ultra speed.
-           */
-          continue;
-        }
-        *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
-        *period = aic7xxx_syncrates[i].period;
-
-        /*
-         * When responding to a target that requests
-         * sync, that rate may fall between two rates
-         * that we can output, but still be a rate
-         * that we can receive.  Because of this,
-         * we may want to respond to the target with
-         * the same rate that it sent to us even
-         * if the period we use to send data to it
-         * is lower.  Only lower the response period
-         * if we must.
-         */
-        if ((i == 0) ||
-           ((aic7xxx_syncrates[i-1].rate & ULTRA_SXFR) != 0
-             && (p->type & AHC_ULTRA) == 0))
-        {
-          response_period = *period;
-        }
-
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
-        {
-          printk(INFO_LEAD "Synchronous at %sMHz, "
-                 "offset %d.\n", p->host_no, channel, target, lun,
-                 aic7xxx_syncrates[i].english, *offset);
-          p->dev_flags[tindex] &= ~ DEVICE_PRINT_SDTR;
-        }
-        break;
+        *period = syncrate->period;
       }
+      break;
     }
+    syncrate++;
   }
-
-  if (i >= num_aic7xxx_syncrates)
+  if ( (*period == 0) || (syncrate->rate[0] == NULL) ||
+       ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) )
   {
     /*
-     * Use asynchronous transfers.
+     * Use async transfers for this target
      */
-    *scsirate = 0;
     *period = 0;
-    *offset = 0;
-    response_period = 0;
-    if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-         (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
-    {
-      printk(INFO_LEAD "Using asynchronous transfers.\n",
-             p->host_no, channel, target, lun);
-      p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
-    }
+    syncrate = NULL;
   }
+  return (syncrate);
+}
 
-  /*
-   * Ensure Ultra mode is set properly for this target.
-   */
-  if ( (*scsirate != 0) && 
-       (aic7xxx_syncrates[i].rate & ULTRA_SXFR) )
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_find_period
+ *
+ * Description:
+ *   Look up the valid SCSIRATE to period conversion in our table
+ *-F*************************************************************************/
+static unsigned int
+aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
+  unsigned int maxsync)
+{
+  struct aic7xxx_syncrate *syncrate;
+
+  if ((p->features & AHC_ULTRA2) != 0)
   {
-    p->ultraenb |= target_mask;
+    scsirate &= SXFR_ULTRA2;
   }
   else
   {
-    p->ultraenb &= ~target_mask;
+    scsirate &= SXFR;
   }
-  if (set)
-  {
-    unsigned char sxfrctl0;
 
-    sxfrctl0 = aic_inb(p, SXFRCTL0);
-    sxfrctl0 &= ~FAST20;
-    if (p->ultraenb & target_mask)
+  syncrate = &aic7xxx_syncrates[maxsync];
+  while (syncrate->rate[0] != NULL)
+  {
+    if ((p->features & AHC_ULTRA2) != 0)
     {
-      sxfrctl0 |= FAST20;
+      if (syncrate->sxfr_ultra2 == 0)
+        break;
+      else if (scsirate == syncrate->sxfr_ultra2)
+        return (syncrate->period);
+    }
+    else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
+    {
+      return (syncrate->period);
     }
-    aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
-    aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1);
-    aic_outb(p, sxfrctl0, SXFRCTL0);
+    syncrate++;
   }
-  return(response_period);
+  return (0); /* async */
 }
 
 /*+F*************************************************************************
  * Function:
- *   scbq_init
+ *   aic7xxx_validate_offset
  *
  * Description:
- *   SCB queue initialization.
- *
+ *   Set a valid offset value for a particular card in use and transfer
+ *   settings in use.
  *-F*************************************************************************/
-static inline void
-scbq_init(volatile scb_queue_type *queue)
+static void
+aic7xxx_validate_offset(struct aic7xxx_host *p,
+  struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide)
 {
-  queue->head = NULL;
-  queue->tail = NULL;
+  unsigned int maxoffset;
+
+  /* Limit offset to what the card (and device) can do */
+  if (syncrate == NULL)
+  {
+    maxoffset = 0;
+  }
+  else if (p->features & AHC_ULTRA2)
+  {
+    maxoffset = MAX_OFFSET_ULTRA2;
+  }
+  else
+  {
+    if (wide)
+      maxoffset = MAX_OFFSET_16BIT;
+    else
+      maxoffset = MAX_OFFSET_8BIT;
+  }
+  *offset = MIN(*offset, maxoffset);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_set_syncrate
+ *
+ * Description:
+ *   Set the actual syncrate down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
+    int target, int channel, unsigned int period, unsigned int offset,
+    unsigned int type)
+{
+  unsigned char tindex;
+  unsigned short target_mask;
+  unsigned char lun;
+  unsigned int old_period, old_offset;
+
+  tindex = target | (channel << 3);
+  target_mask = 0x01 << tindex;
+  lun = aic_inb(p, SCB_TCL) & 0x07;
+
+  if (syncrate == NULL)
+  {
+    period = 0;
+    offset = 0;
+  }
+
+  old_period = p->transinfo[tindex].cur_period;
+  old_offset = p->transinfo[tindex].cur_offset;
+
+  
+  if (type & AHC_TRANS_CUR)
+  {
+    unsigned int scsirate;
+
+    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+    if (p->features & AHC_ULTRA2)
+    {
+      scsirate &= ~SXFR_ULTRA2;
+      if (syncrate != NULL)
+      {
+        scsirate |= syncrate->sxfr_ultra2;
+      }
+      if (type & AHC_TRANS_ACTIVE)
+      {
+        aic_outb(p, offset, SCSIOFFSET);
+      }
+      aic_outb(p, offset, TARG_OFFSET + tindex);
+    }
+    else /* Not an Ultra2 controller */
+    {
+      scsirate &= ~(SXFR|SOFS);
+      p->ultraenb &= ~target_mask;
+      if (syncrate != NULL)
+      {
+        if (syncrate->sxfr & ULTRA_SXFR)
+        {
+          p->ultraenb |= target_mask;
+        }
+        scsirate |= (syncrate->sxfr & SXFR);
+        scsirate |= (offset & SOFS);
+      }
+      if (type & AHC_TRANS_ACTIVE)
+      {
+        unsigned char sxfrctl0;
+
+        sxfrctl0 = aic_inb(p, SXFRCTL0);
+        sxfrctl0 &= ~FAST20;
+        if (p->ultraenb & target_mask)
+          sxfrctl0 |= FAST20;
+        aic_outb(p, sxfrctl0, SXFRCTL0);
+      }
+      aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
+      aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 );
+    }
+    if (type & AHC_TRANS_ACTIVE)
+    {
+      aic_outb(p, scsirate, SCSIRATE);
+    }
+    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+    p->transinfo[tindex].cur_period = period;
+    p->transinfo[tindex].cur_offset = offset;
+    if ( !(type & AHC_TRANS_QUITE) &&
+         (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+         (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+    {
+      if (offset)
+      {
+        int rate_mod = (scsirate & WIDEXFER) ? 1 : 0;
+      
+        printk(INFO_LEAD "Synchronous at %s Mbyte/sec, "
+               "offset %d.\n", p->host_no, channel, target, lun,
+               syncrate->rate[rate_mod], offset);
+      }
+      else
+      {
+        printk(INFO_LEAD "Using asynchronous transfers.\n",
+               p->host_no, channel, target, lun);
+      }
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+    }
+  }
+
+  if (type & AHC_TRANS_GOAL)
+  {
+    p->transinfo[tindex].goal_period = period;
+    p->transinfo[tindex].goal_offset = offset;
+  }
+
+  if (type & AHC_TRANS_USER)
+  {
+    p->transinfo[tindex].user_period = period;
+    p->transinfo[tindex].user_offset = offset;
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_set_width
+ *
+ * Description:
+ *   Set the actual width down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
+    unsigned int width, unsigned int type)
+{
+  unsigned char tindex;
+  unsigned short target_mask;
+  unsigned int old_width, new_offset;
+
+  tindex = target | (channel << 3);
+  target_mask = 1 << tindex;
+  
+  old_width = p->transinfo[tindex].cur_width;
+
+  if (p->features & AHC_ULTRA2)
+    new_offset = MAX_OFFSET_ULTRA2;
+  else if (width == MSG_EXT_WDTR_BUS_16_BIT)
+    new_offset = MAX_OFFSET_16BIT;
+  else
+    new_offset = MAX_OFFSET_8BIT;
+  
+  if (type & AHC_TRANS_CUR) 
+  {
+    unsigned char scsirate;
+
+    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+
+    scsirate &= ~WIDEXFER;
+    if (width == MSG_EXT_WDTR_BUS_16_BIT)
+      scsirate |= WIDEXFER;
+
+    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+
+    if (type & AHC_TRANS_ACTIVE)
+      aic_outb(p, scsirate, SCSIRATE);
+
+    p->transinfo[tindex].cur_width = width;
+
+    if ((aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
+        (p->dev_flags[tindex] & DEVICE_PRINT_WDTR))
+    {
+      printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
+        lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
+    }
+  }
+
+  if (type & AHC_TRANS_GOAL)
+    p->transinfo[tindex].goal_width = width;
+  if (type & AHC_TRANS_USER)
+    p->transinfo[tindex].user_width = width;
+
+  /*
+   * Having just set the width, the SDTR should come next, and we need a valid
+   * offset for the SDTR.  So, we make sure we put a valid one in here now as
+   * the goal_offset.
+   */
+  if (p->transinfo[tindex].goal_offset)
+    p->transinfo[tindex].goal_offset = new_offset;
+
+}
+      
+/*+F*************************************************************************
+ * Function:
+ *   scbq_init
+ *
+ * Description:
+ *   SCB queue initialization.
+ *
+ *-F*************************************************************************/
+static void
+scbq_init(volatile scb_queue_type *queue)
+{
+  queue->head = NULL;
+  queue->tail = NULL;
 }
 
 /*+F*************************************************************************
@@ -1837,7 +2424,7 @@
  *   Remove an SCB from the head of the list.
  *
  *-F*************************************************************************/
-static __inline struct aic7xxx_scb *
+static inline struct aic7xxx_scb *
 scbq_remove_head(volatile scb_queue_type *queue)
 {
   struct aic7xxx_scb * scbp;
@@ -2030,7 +2617,7 @@
  * Description:
  *   Set the specified target busy.
  *-F*************************************************************************/
-static __inline void
+static inline void
 aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
   p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
@@ -2044,7 +2631,7 @@
  *   Returns the index of the busy target, and optionally sets the
  *   target inactive.
  *-F*************************************************************************/
-static __inline unsigned char
+static inline unsigned char
 aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
     int unbusy)
 {
@@ -2100,47 +2687,47 @@
  * Description:
  *   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, int force_alloc)
+static int
+aic7xxx_allocate_scb(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb   *scbp = NULL;
   int scb_size = sizeof(struct aic7xxx_scb) +
                  sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
   int i;
+  int step = PAGE_SIZE / 1024;
   unsigned long scb_count = 0;
   struct hw_scatterlist *hsgp;
   struct aic7xxx_scb *scb_ap;
+  unsigned long temp;
 
 
-  if (force_alloc == FALSE)
-  {
-    scbp = scbq_remove_head(&p->scb_data->free_scbs);
-    if (scbp != NULL)
-      return(scbp);
-  }
-  /*
-   * Either there wasn't an SCB or this is a strictly allocation call
-   */
-
   if (p->scb_data->numscbs < p->scb_data->maxscbs)
   {
-
     /*
-     * Optimize for 30 scbs at a time, but allow a final allocation of
-     * fewer than 30 scbs.  Except on 64 bit platforms, we optimize for
-     * 29 SCBs at a time because a pointer is 4 bytes larger and we don't
-     * want to overrun this suppossedly 32K allocation to 64K and waste
-     * tons of space.
+     * Calculate the optimal number of SCBs to allocate.
+     *
+     * NOTE: This formula works because the sizeof(sg_array) is always
+     * 1024.  Therefore, scb_size * i would always be > PAGE_SIZE *
+     * (i/step).  The (i-1) allows the left hand side of the equation
+     * to grow into the right hand side to a point of near perfect
+     * efficiency since scb_size * (i -1) is growing slightly faster
+     * than the right hand side.  If the number of SG array elements
+     * is changed, this function may not be near so efficient any more.
      */
-    if( sizeof(void *) == sizeof(int) )
-      scb_count = MIN(30, p->scb_data->maxscbs - p->scb_data->numscbs);
-    else
-      scb_count = MIN(29, p->scb_data->maxscbs - p->scb_data->numscbs);
-    
+    for ( i=step;; i *= 2 )
+    {
+      if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) )
+      {
+        i /= 2;
+        break;
+      }
+    }
+    scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
     scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC);
     if (scb_ap != NULL)
     {
-      if (aic7xxx_verbose & VERBOSE_QUEUE)
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
       {
         if (p->scb_data->numscbs == 0)
           printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
@@ -2149,8 +2736,12 @@
           printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
             p->host_no, -1, -1, -1, scb_count);
       }
+#endif
       memset(scb_ap, 0, scb_count * scb_size);
-      hsgp = (struct hw_scatterlist *) &scb_ap[scb_count];
+      temp = (unsigned long) &scb_ap[scb_count];
+      temp += 1023;
+      temp &= ~1023;
+      hsgp = (struct hw_scatterlist *)temp;
       for (i=0; i < scb_count; i++)
       {
         scbp = &scb_ap[i];
@@ -2164,20 +2755,14 @@
         p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
         scbq_insert_head(&p->scb_data->free_scbs, scbp);
       }
+      scbp->kmalloc_ptr = scb_ap;
     }
     else
     {
-      return(NULL);
+      return(0);
     }
   }
-  if (force_alloc == TRUE)
-  {
-    return((struct aic7xxx_scb *)scb_count);
-  }
-  else
-  {
-    return(scbq_remove_head(&p->scb_data->free_scbs));
-  }
+  return(scb_count);
 }
 
 /*+F*************************************************************************
@@ -2189,7 +2774,7 @@
  *   to queue completed commands, then call scsi_done() on them when
  *   we're finished.  This function queues the completed commands.
  *-F*************************************************************************/
-static inline void
+static void
 aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
   cmd->host_scribble = (char *)p->completeq.head;
@@ -2203,7 +2788,7 @@
  * Description:
  *   Process the completed command queue.
  *-F*************************************************************************/
-static inline void
+static void
 aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
 {
   Scsi_Cmnd *cmd;
@@ -2276,17 +2861,15 @@
   }
   if (scb->flags & SCB_RESET)
   {
-      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
-        (cmd->result & 0xffff);
+      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
   }
   else if (scb->flags & SCB_ABORT)
   {
-      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
-        (cmd->result & 0xffff);
+      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
   }
-  if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+  else if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
   {
-    if(cmd->cmnd[0] == INQUIRY)
+    if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) )
     {
       char *buffer;
       
@@ -2304,35 +2887,65 @@
 #define WIDE_INQUIRY_BITS 0x60
 #define SYNC_INQUIRY_BITS 0x10
       if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
-           (p->needwdtr_copy & (1<<tindex)) &&
-           (p->type & AHC_WIDE) )
+           (p->features & AHC_WIDE) )
       {
         p->needwdtr |= (1<<tindex);
         p->needwdtr_copy |= (1<<tindex);
-        p->syncinfo[tindex].offset = MAX_OFFSET_16BIT;
+        if ( (p->flags & AHC_SEEPROM_FOUND) &&
+             (p->transinfo[tindex].user_width != MSG_EXT_WDTR_BUS_16_BIT) )
+          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
+        else
+          p->transinfo[tindex].goal_width = MSG_EXT_WDTR_BUS_16_BIT;
       }
       else
       {
         p->needwdtr &= ~(1<<tindex);
         p->needwdtr_copy &= ~(1<<tindex);
-        p->syncinfo[tindex].offset = MAX_OFFSET_8BIT;
+        pause_sequencer(p);
+        aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun,
+                          MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE |
+                                                   AHC_TRANS_GOAL |
+                                                   AHC_TRANS_CUR) );
+        unpause_sequencer(p, FALSE);
       }
       if (buffer[7] & SYNC_INQUIRY_BITS)
       {
         p->needsdtr |= (1<<tindex);
         p->needsdtr_copy |= (1<<tindex);
+
+        if (p->flags & AHC_SEEPROM_FOUND)
+          p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
+        else if (p->features & AHC_ULTRA2)
+          p->transinfo[tindex].goal_period =
+            aic7xxx_syncrates[AHC_SYNCRATE_ULTRA2].period;
+        else if (p->features & AHC_ULTRA)
+          p->transinfo[tindex].goal_period =
+            aic7xxx_syncrates[AHC_SYNCRATE_ULTRA].period;
+        else
+          p->transinfo[tindex].goal_period =
+            aic7xxx_syncrates[AHC_SYNCRATE_FAST].period;
+
+        if (p->features & AHC_ULTRA2)
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+        else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+        else
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
       }
       else
       {
         p->needsdtr &= ~(1<<tindex);
         p->needsdtr_copy &= ~(1<<tindex);
+        p->transinfo[tindex].goal_period = 0;
+        p->transinfo[tindex].goal_offset = 0;
       }
       p->dev_flags[tindex] |= DEVICE_SCANNED;
+      p->dev_flags[tindex] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
 #undef WIDE_INQUIRY_BITS
 #undef SYNC_INQUIRY_BITS
     }
   }
-  if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  else if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
   {
     unsigned short mask;
     int message_error = FALSE;
@@ -2355,7 +2968,7 @@
       p->wdtr_pending &= ~mask;
       if (message_error)
       {
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
              (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) )
         {
           printk(INFO_LEAD "Device failed to complete Wide Negotiation "
@@ -2375,7 +2988,7 @@
       p->sdtr_pending &= ~mask;
       if (message_error)
       {
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
              (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
         {
           printk(INFO_LEAD "Device failed to complete Sync Negotiation "
@@ -2411,12 +3024,17 @@
       {
         scbq_insert_tail(&p->waiting_scbs, scbp);
       }
-    }
-    if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp )
-    {
-      scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
-      if (scbp)
-        scbq_insert_tail(&p->waiting_scbs, scbp);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n",
+               p->host_no, CTL_OF_SCB(scbp));
+#endif
+      if (queue_depth > p->dev_active_cmds[tindex])
+      {
+        scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+        if (scbp)
+          scbq_insert_tail(&p->waiting_scbs, scbp);
+      }
     }
   }
   if ( !(scb->tag_action) && (p->tagenable & (1<<tindex)) )
@@ -2436,7 +3054,6 @@
     aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, TRUE);
   }
 
-#ifdef AIC7XXX_PROC_STATS
   {
     int actual;
 
@@ -2454,11 +3071,17 @@
     if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
     {
       struct aic7xxx_xferstats *sp;
+#ifdef AIC7XXX_PROC_STATS
       long *ptr;
       int x;
+#endif /* AIC7XXX_PROC_STATS */
 
       sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
       sp->xfers++;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if ( (sp->xfers > 16) && (aic7xxx_verbose > 0xffff) )
+        aic7xxx_verbose &= 0xffff;
+#endif
 
       /*
        * For block devices, cmd->request.cmd is always == either READ or
@@ -2471,14 +3094,19 @@
       {
         sp->w_total++;
         sp->w_total512 += (actual >> 9);
+#ifdef AIC7XXX_PROC_STATS
         ptr = sp->w_bins;
+#endif /* AIC7XXX_PROC_STATS */
       }
       else
       {
         sp->r_total++;
         sp->r_total512 += (actual >> 9);
+#ifdef AIC7XXX_PROC_STATS
         ptr = sp->r_bins;
+#endif /* AIC7XXX_PROC_STATS */
       }
+#ifdef AIC7XXX_PROC_STATS
       for (x = 9; x <= 17; x++)
       {
         if (actual < (1 << x))
@@ -2491,10 +3119,9 @@
       {
         ptr[x - 9]++;
       }
+#endif /* AIC7XXX_PROC_STATS */
     }
   }
-#endif /* AIC7XXX_PROC_STATS */
-
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
@@ -2676,7 +3303,10 @@
   {
     p->qinfifo[qinpos++] = SCB_LIST_NULL;
   }
-  aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+  if (p->features & AHC_QUEUE_REGS)
+    aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+  else
+    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
 
   return (found);
 }
@@ -2749,7 +3379,7 @@
       {
         case 0:
                  min_target = 0;
-                 max_target = (p->type & AHC_WIDE) ? 15 : 7;
+                 max_target = (p->features & AHC_WIDE) ? 15 : 7;
                  break;
         case 1:
                  min_target = 8;
@@ -2758,7 +3388,7 @@
         case ALL_CHANNELS:
         default:
                  min_target = 0;
-                 max_target = (p->type & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
+                 max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
                  break;
       }
     }
@@ -2821,7 +3451,7 @@
       j = 0; 
       prev_scbp = NULL; 
       scbp = p->delayed_scbs[i].head;
-      while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+      while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
       {
         prev_scbp = scbp;
         scbp = scbp->q_next;
@@ -2846,7 +3476,7 @@
           prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
         }
       }
-      if ( j > p->scb_data->numscbs )
+      if ( j > (p->scb_data->maxscbs + 1) )
       {
         if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
           printk(WARN_LEAD "Yikes!! There's a loop in the "
@@ -2880,7 +3510,7 @@
     j = 0; 
     prev_scbp = NULL; 
     scbp = p->waiting_scbs.head;
-    while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+    while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
     {
       prev_scbp = scbp;
       scbp = scbp->q_next;
@@ -2905,7 +3535,7 @@
         prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
       }
     }
-    if ( j > p->scb_data->numscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
         printk(WARN_LEAD "Yikes!! There's a loop in the "
@@ -2927,7 +3557,7 @@
     next = aic_inb(p, WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
     j = 0;
-    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
     {
       aic_outb(p, next, SCBPTR);
       scb_index = aic_inb(p, SCB_TAG);
@@ -2975,7 +3605,7 @@
         }
       }
     }
-    if ( j > p->scb_data->maxhscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       printk(WARN_LEAD "Yikes!!  There is a loop in the waiting for "
         "selection list!\n", p->host_no, channel, target, lun);
@@ -2990,13 +3620,14 @@
   if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
     printk(INFO_LEAD "Cleaning disconnected scbs "
       "list.\n", p->host_no, channel, target, lun);
+  if (p->features & AHC_PAGESCBS)
   {
     unsigned char next, prev, scb_index;
 
     next = aic_inb(p, DISCONNECTED_SCBH);
     prev = SCB_LIST_NULL;
     j = 0;
-    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
     {
       aic_outb(p, next, SCBPTR);
       scb_index = aic_inb(p, SCB_TAG);
@@ -3029,7 +3660,7 @@
         }
       }
     }
-    if ( j > p->scb_data->maxhscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       printk(WARN_LEAD "Yikes!!  There is a loop in the disconnected list!\n",
         p->host_no, channel, target, lun);
@@ -3041,15 +3672,23 @@
    * Walk the free list making sure no entries on the free list have
    * a valid SCB_TAG value or SCB_CONTROL byte.
    */
+  if (p->features & AHC_PAGESCBS)
   {
     unsigned char next;
 
     j = 0;
     next = aic_inb(p, FREE_SCBH);
-    while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) )
+    if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) )
+    {
+      printk(WARN_LEAD "Bogus FREE_SCBH!.\n", p->host_no, channel,
+        target, lun);
+      init_lists = TRUE;
+      next = SCB_LIST_NULL;
+    }
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
     {
       aic_outb(p, next, SCBPTR);
-      if ( aic_inb(p, SCB_TAG) < p->scb_data->numscbs )
+      if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
       {
         printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
           target, lun);
@@ -3063,7 +3702,7 @@
         next = aic_inb(p, SCB_NEXT);
       }
     }
-    if ( j > p->scb_data->maxhscbs )
+    if ( j > (p->scb_data->maxscbs + 1) )
     {
       printk(WARN_LEAD "Yikes!!  There is a loop in the free list!\n",
         p->host_no, channel, target, lun);
@@ -3081,7 +3720,7 @@
     aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
     aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
   }
-  for (i = p->scb_data->maxhscbs; i >= 0; --i)
+  for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
   {
     unsigned char scbid;
 
@@ -3157,7 +3796,7 @@
   aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
   aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
        CLRPHASECHG | CLRREQINIT, CLRSINT1);
-  aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT, CLRINT);
+  aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
 }
 
 /*+F*************************************************************************
@@ -3170,26 +3809,31 @@
 static void
 aic7xxx_reset_current_bus(struct aic7xxx_host *p)
 {
-  unsigned char scsiseq;
 
   /* Disable reset interrupts. */
   aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
 
+  /* Turn off the bus' current operations, after all, we shouldn't have any
+   * valid commands left to cause a RSELI and SELO once we've tossed the
+   * bus away with this reset, so we might as well shut down the sequencer
+   * until the bus is restarted as oppossed to saving the current settings
+   * and restoring them (which makes no sense to me). */
+
   /* Turn on the bus reset. */
-  scsiseq = aic_inb(p, SCSISEQ);
-  aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ);
+  aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
+  while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
+    mdelay(5);
 
-  udelay(5000);
+  mdelay(10);
 
   /* Turn off the bus reset. */
-  aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ);
+  aic_outb(p, 0, SCSISEQ);
+  mdelay(5);
 
   aic7xxx_clear_intstat(p);
-
   /* Re-enable reset interrupts. */
   aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
 
-  udelay(2000);
 }
 
 /*+F*************************************************************************
@@ -3202,7 +3846,7 @@
 static void
 aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
 {
-  unsigned long offset, offset_max;
+  unsigned long offset_min, offset_max;
   unsigned char sblkctl;
   int cur_channel;
 
@@ -3215,48 +3859,52 @@
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
     p->sdtr_pending &= 0x00FF;
-    offset = TARG_SCRATCH + 8;
-    offset_max = TARG_SCRATCH + 16;
+    offset_min = 8;
+    offset_max = 16;
   }
   else
   {
-    if (p->type & AHC_WIDE)
+    if (p->features & AHC_WIDE)
     {
       p->needsdtr = p->needsdtr_copy;
       p->needwdtr = p->needwdtr_copy;
       p->sdtr_pending = 0x0;
       p->wdtr_pending = 0x0;
-      offset = TARG_SCRATCH;
-      offset_max = TARG_SCRATCH + 16;
+      offset_min = 0;
+      offset_max = 16;
     }
     else
     {
       /* Channel A */
       p->needsdtr |= (p->needsdtr_copy & 0x00FF);
       p->sdtr_pending &= 0xFF00;
-      offset = TARG_SCRATCH;
-      offset_max = TARG_SCRATCH + 8;
+      offset_min = 0;
+      offset_max = 8;
     }
   }
 
-  while (offset < offset_max)
+  while (offset_min < offset_max)
   {
     /*
      * Revert to async/narrow transfers until we renegotiate.
      */
-    u_char targ_scratch;
-
-    targ_scratch = aic_inb(p, offset);
-    targ_scratch &= SXFR;
-    aic_outb(p, targ_scratch, offset++);
+    aic_outb(p, 0, TARG_SCSIRATE + offset_min);
+    if (p->features & AHC_ULTRA2)
+    {
+      aic_outb(p, 0, TARG_OFFSET + offset_min);
+    }
+    offset_min++;
   }
 
   /*
    * Reset the bus and unpause/restart the controller
    */
   sblkctl = aic_inb(p, SBLKCTL);
-  cur_channel = (sblkctl & SELBUSB) >> 3;
-  if (cur_channel != channel)
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+    cur_channel = (sblkctl & SELBUSB) >> 3;
+  else
+    cur_channel = 0;
+  if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
   {
     /*
      * Case 1: Command for another bus is active
@@ -3273,7 +3921,7 @@
     {
       aic7xxx_reset_current_bus(p);
     }
-    aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
     aic7xxx_clear_intstat(p);
     aic_outb(p, sblkctl, SBLKCTL);
   }
@@ -3294,7 +3942,7 @@
     {
       aic7xxx_reset_current_bus(p);
     }
-    aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
     aic7xxx_clear_intstat(p);
   }
   if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
@@ -3305,7 +3953,13 @@
    */
   aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
-  if ( !(p->type & AHC_TWIN) )
+  /*
+   * Convince Mid Level SCSI code to leave us be for a little bit...
+   */
+  p->last_reset = jiffies;
+  p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY));
+
+  if ( !(p->features & AHC_TWIN) )
   {
     restart_sequencer(p);
   }
@@ -3321,7 +3975,7 @@
  *   Scan the awaiting_scbs queue downloading and starting as many
  *   scbs as we can.
  *-F*************************************************************************/
-static inline void
+static void
 aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb *scb;
@@ -3346,23 +4000,38 @@
     tindex = TARGET_INDEX(scb->cmd);
     if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
     {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Reducing Queue depth for untagged command.\n",
+               p->host_no, CTL_OF_SCB(scb));
+#endif
       p->dev_temp_queue_depth[tindex] = 1;
     }
     if ( (p->dev_active_cmds[tindex] >=
           p->dev_temp_queue_depth[tindex]) ||
          (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) )
     {
-        scbq_insert_tail(&p->delayed_scbs[tindex], scb);
-        if ( !(p->dev_timer[tindex].expires) &&
-             !(p->dev_active_cmds[tindex]) )
-        {
-          p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ);
-          add_timer(&p->dev_timer[tindex]);
-        }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Moving SCB to Delayed Queue.\n",
+               p->host_no, CTL_OF_SCB(scb));
+#endif
+      scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+      if ( !(p->dev_timer[tindex].expires) &&
+           !(p->dev_active_cmds[tindex]) )
+      {
+        p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ);
+        add_timer(&p->dev_timer[tindex]);
+      }
     }
     else
     {
         scb->flags &= ~SCB_WAITINGQ;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if (aic7xxx_verbose > 0xffff)
+          printk(INFO_LEAD "Sending command %d/0x%x to QINFIFO\n", p->host_no,
+                 CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+#endif
         p->dev_active_cmds[tindex]++;
         p->activescbs++;
         if ( !(scb->tag_action) )
@@ -3375,15 +4044,95 @@
   }
   if (sent)
   {
-    pause_sequencer(p);
-    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-    unpause_sequencer(p, FALSE);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (aic7xxx_verbose > 0xffff)
+    {
+      printk(INFO_LEAD "Sending commands to QINFIFO\n", p->host_no,
+             -1, -1, -1);
+      if ( (p->isr_count < 16) && (aic7xxx_panic_on_abort) &&
+           (p->flags & AHC_PAGESCBS) )
+        aic7xxx_check_scbs(p, "While sending commands to QINFIFO");
+    }
+#endif
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+    {
+      pause_sequencer(p);
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+      unpause_sequencer(p, FALSE);
+    }
     if (p->activescbs > p->max_activescbs)
       p->max_activescbs = p->activescbs;
   }
   DRIVER_UNLOCK
 }
 
+#ifdef CONFIG_PCI
+
+#define  DPE 0x80
+#define  SSE 0x40
+#define  RMA 0x20
+#define  RTA 0x10
+#define  STA 0x08
+#define  DPR 0x01
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_pci_intr
+ *
+ * Description:
+ *   Check the scsi card for PCI errors and clear the interrupt
+ *
+ *   NOTE: If you don't have this function and a 2940 card encounters
+ *         a PCI error condition, the machine will end up locked as the
+ *         interrupt handler gets slammed with non-stop PCI error interrupts
+ *-F*************************************************************************/
+static void
+aic7xxx_pci_intr(struct aic7xxx_host *p)
+{
+  unsigned char status1;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
+#else
+  pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+                           PCI_STATUS + 1, &status1);
+#endif
+
+  if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+      "phase.\n", p->host_no, -1, -1, -1);
+  if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+      "PERR#\n", p->host_no, -1, -1, -1);
+  
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+  pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
+#else
+  pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+                            PCI_STATUS + 1, status1);
+#endif
+  if (status1 & (DPR|RMA|RTA))
+    aic_outb(p,  CLRPARERR, CLRINT);
+
+  if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
+    aic7xxx_panic_abort(p, NULL);
+
+}
+#endif /* CONFIG_PCI */
 
 /*+F*************************************************************************
  * Function:
@@ -3554,12 +4303,11 @@
 aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
 {
   unsigned short targ_mask;
-  unsigned char  targ_scratch;
-  int scratch_offset = target;
+  unsigned char tindex = target;
 
-  scratch_offset += channel << 3;
+  tindex |= ((channel & 0x01) << 3);
 
-  targ_mask = (0x01 << scratch_offset);
+  targ_mask = (0x01 << tindex);
   /*
    * Go back to async/narrow transfers and renegotiate.
    */
@@ -3567,9 +4315,9 @@
   p->needwdtr |= (p->needwdtr_copy & targ_mask);
   p->sdtr_pending &= ~targ_mask;
   p->wdtr_pending &= ~targ_mask;
-  targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
-  targ_scratch &= SXFR;
-  aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
+  aic_outb(p, 0, TARG_SCSIRATE + tindex);
+  if (p->features & AHC_ULTRA2)
+    aic_outb(p, 0, TARG_OFFSET + tindex);
   aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
   if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
     printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
@@ -3589,20 +4337,30 @@
 {
   struct aic7xxx_scb *scb;
   unsigned short target_mask;
-  unsigned char target, scratch_offset, lun;
+  unsigned char target, lun, tindex;
   unsigned char queue_flag = FALSE;
   char channel;
 
   target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
-  channel = (aic_inb(p, SBLKCTL) >> 3) & 0x01;
-  scratch_offset = target + (channel << 3);
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+    channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+  else
+    channel = 0;
+  tindex = target + (channel << 3);
   lun = aic_inb(p, SAVED_TCL) & 0x07;
-  target_mask = (0x01 << scratch_offset);
+  target_mask = (0x01 << tindex);
 
+  /*
+   * Go ahead and clear the SEQINT now, that avoids any interrupt race
+   * conditions later on in case we enable some other interrupt.
+   */
+  aic_outb(p, CLRSEQINT, CLRINT);
   switch (intstat & SEQINT_MASK)
   {
     case NO_MATCH:
       {
+        aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+                 SCSISEQ);
         printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
                "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
         printk(WARN_LEAD "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
@@ -3663,12 +4421,11 @@
         p->msg_len = 0;
         p->msg_index = 0;
 
-       /*
-        * We have to clear the SEQINT *BEFORE* we set the REQINIT handler
-        * active or else VLB and edge triggered EISA cards could loose the
-        * first REQINIT and cause a bus hang/reset cycle.
-        */
-        aic_outb(p, CLRSEQINT, CLRINT);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if (aic7xxx_verbose > 0xffff)
+          printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
+                 channel, target, lun);
+#endif
 
        /*      
         * To actually receive the message, simply turn on
@@ -3691,13 +4448,11 @@
          * or WDTR message for this target. If we did, this is a
          * signal that the target is refusing negotiation.
          */
-        unsigned char targ_scratch;
         unsigned char scb_index;
         unsigned char last_msg;
 
         scb_index = aic_inb(p, SCB_TAG);
         scb = p->scb_data->scb_array[scb_index];
-        targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
         last_msg = aic_inb(p, LAST_MSG);
 
         if ( (last_msg == MSG_IDENTIFYFLAG) &&
@@ -3705,7 +4460,7 @@
             !(scb->flags & SCB_MSGOUT_BITS) )
         {
           if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
-              (p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS))
+              (p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS))
           {
             /*
              * OK...the device seems able to accept tagged commands, but
@@ -3727,7 +4482,7 @@
             aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
           }
           else if ( (scb->tag_action == MSG_SIMPLE_Q_TAG) &&
-                   !(p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS) )
+                   !(p->dev_flags[tindex] & DEVICE_TAGGED_SUCCESS) )
           {
             unsigned char i, reset = 0;
             struct aic7xxx_scb *scbp;
@@ -3742,8 +4497,8 @@
              */
             p->tagenable &= ~target_mask;
             p->orderedtag &= ~target_mask;
-            p->dev_max_queue_depth[scratch_offset] =
-               p->dev_temp_queue_depth[scratch_offset] = 1;
+            p->dev_max_queue_depth[tindex] =
+               p->dev_temp_queue_depth[tindex] = 1;
             /*
              * We set this command up as a bus device reset.  However, we have
              * to clear the tag type as it's causing us problems.  We shouldnt
@@ -3792,40 +4547,34 @@
           /*
            * note 8bit xfers and clear flag
            */
-          targ_scratch &= 0x7F;
           p->needwdtr &= ~target_mask;
           p->needwdtr_copy &= ~target_mask;
           p->wdtr_pending &= ~target_mask;
-          if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-               (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+            (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+          if ( (p->needsdtr_copy & target_mask) &&
+               !(p->sdtr_pending & target_mask) )
           {
-            printk(INFO_LEAD "Refusing WIDE negotiation; using 8 bit "
-              "transfers.\n", p->host_no, CTL_OF_SCB(scb));
-            p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-          }
-          scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
-          p->syncinfo[scratch_offset].offset = MAX_OFFSET_8BIT;
-          if (p->needsdtr_copy & target_mask)
-            p->needsdtr |= target_mask;
+            p->sdtr_pending |= target_mask;
+            scb->flags |= SCB_MSGOUT_SDTR;
+            aic_outb(p, HOST_MSG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+          }
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
          /*
           * note asynch xfers and clear flag
           */
-          targ_scratch &= 0xF0;
           p->needsdtr &= ~target_mask;
           p->needsdtr_copy &= ~target_mask;
           p->sdtr_pending &= ~target_mask;
-          p->syncinfo[scratch_offset].period = 0;
-          p->syncinfo[scratch_offset].offset = 0;
-          if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-               (p->dev_flags[scratch_offset] & DEVICE_PRINT_SDTR) )
-          {
-            printk(INFO_LEAD "Refusing synchronous negotiation; using "
-              "asynchronous transfers.\n", p->host_no, CTL_OF_SCB(scb));
-            p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_SDTR;
-          }
+          scb->flags &= ~SCB_MSGOUT_SDTR;
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+            (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
         }
         else if (aic7xxx_verbose & VERBOSE_SEQINT)
         {
@@ -3835,8 +4584,6 @@
           printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause.  "
             "Ignoring.\n", p->host_no, channel, target, lun);
         }
-        aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
-        aic_outb(p, targ_scratch, SCSIRATE);
       }
       break;
 
@@ -3851,11 +4598,15 @@
          * the sequencer running in the common case of command completes
          * without error.  The sequencer will have DMA'd the SCB back
          * up to us, so we can reference the drivers SCB array.
+         *
+         * Set the default return value to 0 indicating not to send
+         * sense.  The sense code will change this if needed and this
+         * reduces code duplication.
          */
+        aic_outb(p, 0, RETURN_1);
         scb_index = aic_inb(p, SCB_TAG);
         if (scb_index > p->scb_data->numscbs)
         {
-          aic_outb(p, 0, RETURN_1);
           printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
             p->host_no, channel, target, lun, intstat, scb_index);
           break;
@@ -3863,12 +4614,6 @@
         scb = p->scb_data->scb_array[scb_index];
         hscb = scb->hscb;
 
-        /*
-         * Set the default return value to 0 indicating not to send
-         * sense.  The sense code will change this if needed and this
-         * reduces code duplication.
-         */
-        aic_outb(p, 0, RETURN_1);
         if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
         {
           printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
@@ -3937,7 +4682,6 @@
 
                 scb->sg_count = hscb->SG_segment_count = 1;
                 scb->sg_length = sizeof(cmd->sense_buffer);
-                scb->flags &= ~SCB_MSGOUT_BITS;
                 scb->tag_action = 0;
                 /*
                  * This problem could be caused if the target has lost power
@@ -3945,12 +4689,67 @@
                  * so if needed, we'll re-negotiate while doing the sense cmd.
                  * However, if this SCB already was attempting to negotiate,
                  * then we assume this isn't the problem and skip this part.
-                 *
-                 * 1998/04/23 - We also don't want to set the flag if the
-                 *   original command was a TEST_UNIT_READY since that
-                 *   implies a SEND_SENSE anyway.
                  */
-                if (scb->cmd->cmnd[0] != TEST_UNIT_READY)
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+                if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
+                     (p->dev_flags[tindex] & DEVICE_SCANNED) &&
+                     !(p->wdtr_pending & target_mask) && 
+                     !(p->sdtr_pending & target_mask) )
+                {
+                  p->needwdtr |= (p->needwdtr_copy & target_mask);
+                  p->needsdtr |= (p->needsdtr_copy & target_mask);
+                }
+                else if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) ||
+                          (scb->cmd == p->dev_sdtr_cmnd[tindex]) )
+                {
+                  /*
+                   * This is already a negotiation command, so we must have
+                   * already done either WDTR or SDTR (or maybe both).  So
+                   * we simply check sdtr_pending and needsdtr to see if we
+                   * should throw out SDTR on this command.
+                   *
+                   * Note: Don't check the needsdtr_copy here, instead just
+                   * check to see if WDTR wiped out our SDTR and set needsdtr.
+                   * Even if WDTR did wipe out SDTR and set needsdtr, if
+                   * parse_msg() then turned around and started our SDTR
+                   * in back to back fasion, then conclusion of that should
+                   * have negated any needsdtr setting.  That's why we only
+                   * check needsdtr and sdtr_pending.
+                   */
+                  scb->flags &= ~SCB_MSGOUT_BITS;
+                  if ( (scb->cmd == p->dev_wdtr_cmnd[tindex]) &&
+                       !(p->sdtr_pending & target_mask) &&
+                       (p->needsdtr & target_mask) )
+                  {
+                    p->sdtr_pending |= target_mask;
+                    hscb->control |= MK_MESSAGE;
+                    scb->flags |= SCB_MSGOUT_SDTR;
+                  }
+
+                  /*
+                   * This is the important part though.  We are getting sense
+                   * info back from this device.  It's going into a fake
+                   * command.  We need to put that into the real command
+                   * instead so that the mid level SCSI code can act upon it.
+                   * So, when we set up these fake commands, the next pointer
+                   * is used to point to the real command.  Use that to change
+                   * the address of our sense_buffer[] to the real command.
+                   * However, don't do this if the real command is also a
+                   * TEST_UNIT_READY as it will most likely pull down its own
+                   * SENSE information anyway.
+                   */
+                  if (cmd->next->cmnd[0] != TEST_UNIT_READY)
+                  {
+                    scb->sg_list[0].address = 
+                      cpu_to_le32(VIRT_TO_BUS(&cmd->next->sense_buffer[0]));
+                    hscb->data_pointer = scb->sg_list[0].address;
+                  }
+                }
+#else
+                if (  (scb->cmd->cmnd[0] != TEST_UNIT_READY) &&
+                     !(scb->flags & SCB_MSGOUT_BITS) && 
+                      (scb->cmd->lun == 0) &&
+                      (p->dev_flags[TARGET_INDEX(scb->cmd)] & DEVICE_SCANNED) )
                 {
                   if ( (p->needwdtr_copy & target_mask) &&
                       !(p->wdtr_pending & target_mask) &&
@@ -3959,7 +4758,7 @@
                     p->needwdtr |= target_mask;
                     p->wdtr_pending |= target_mask;
                     hscb->control |= MK_MESSAGE;
-                    scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+                    scb->flags |= SCB_MSGOUT_WDTR;
                   }
                   if ( p->needsdtr_copy & target_mask )
                   {
@@ -3973,12 +4772,26 @@
                     }
                   }
                 }
-
+                else
+                  scb->flags &= ~SCB_MSGOUT_BITS;
+#endif /* AIC7XXX_FAKE_NEGOTIATION_CMDS */
                 scb->flags |= SCB_SENSE;
                 /*
                  * Ensure the target is busy since this will be an
                  * an untagged request.
                  */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+                if (aic7xxx_verbose > 0xffff)
+                {
+                  if (scb->flags & SCB_MSGOUT_BITS)
+                    printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
+                           CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
+                           "SDTR" : "WDTR");
+                  else
+                    printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
+                           CTL_OF_SCB(scb));
+                }
+#endif
                 aic7xxx_busy_target(p, scb);
                 aic_outb(p, SEND_SENSE, RETURN_1);
                 aic7xxx_error(cmd) = DID_OK;
@@ -4003,7 +4816,7 @@
                */
               aic7xxx_search_qinfifo(p, target, channel, lun,
                 SCB_LIST_NULL, 0, TRUE,
-                &p->delayed_scbs[scratch_offset]);
+                &p->delayed_scbs[tindex]);
               next_scbp = p->waiting_scbs.head;
               while ( next_scbp != NULL )
               {
@@ -4013,7 +4826,7 @@
                      SCB_LIST_NULL) )
                 {
                   scbq_remove(&p->waiting_scbs, prev_scbp);
-                  scbq_insert_tail(&p->delayed_scbs[scratch_offset],
+                  scbq_insert_tail(&p->delayed_scbs[tindex],
                     prev_scbp);
                 }
               }
@@ -4033,15 +4846,15 @@
                   {
                     if (next_scbp->flags & SCB_WAITINGQ)
                     {
-                      p->dev_active_cmds[scratch_offset]++;
+                      p->dev_active_cmds[tindex]++;
                       p->activescbs--;
-                      scbq_remove(&p->delayed_scbs[scratch_offset], next_scbp);
+                      scbq_remove(&p->delayed_scbs[tindex], next_scbp);
                       scbq_remove(&p->waiting_scbs, next_scbp);
                     }
-                    scbq_insert_head(&p->delayed_scbs[scratch_offset],
+                    scbq_insert_head(&p->delayed_scbs[tindex],
                       next_scbp);
                     next_scbp->flags |= SCB_WAITINGQ;
-                    p->dev_active_cmds[scratch_offset]--;
+                    p->dev_active_cmds[tindex]--;
                     p->activescbs--;
                     next_hscb = aic_inb(p, SCB_NEXT);
                     aic_outb(p, 0, SCB_CONTROL);
@@ -4074,65 +4887,67 @@
               aic_outb(p, active_hscb, SCBPTR);
               if (scb->flags & SCB_WAITINGQ)
               {
-                scbq_remove(&p->delayed_scbs[scratch_offset], scb);
+                scbq_remove(&p->delayed_scbs[tindex], scb);
                 scbq_remove(&p->waiting_scbs, scb);
-                p->dev_active_cmds[scratch_offset]++;
+                p->dev_active_cmds[tindex]++;
                 p->activescbs++;
               }
-              scbq_insert_head(&p->delayed_scbs[scratch_offset], scb);
-              p->dev_active_cmds[scratch_offset]--;
+              scbq_insert_head(&p->delayed_scbs[tindex], scb);
+              p->dev_active_cmds[tindex]--;
               p->activescbs--;
               scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
                   
-              if (p->dev_timer[scratch_offset].expires == 0) 
+              if (p->dev_timer[tindex].expires == 0) 
               {
-                if ( p->dev_active_cmds[scratch_offset] )
+                if ( p->dev_active_cmds[tindex] )
                 {
-                  p->dev_timer[scratch_offset].expires = jiffies + (HZ * 2);
-                  add_timer(&p->dev_timer[scratch_offset]);
+                  p->dev_timer[tindex].expires = jiffies + (HZ * 2);
+                  add_timer(&p->dev_timer[tindex]);
                 }
                 else
                 {
-                  p->dev_timer[scratch_offset].expires = jiffies + (HZ / 2);
-                  add_timer(&p->dev_timer[scratch_offset]);
+                  p->dev_timer[tindex].expires = jiffies + (HZ / 2);
+                  add_timer(&p->dev_timer[tindex]);
                 }
               }
-              if (aic7xxx_verbose & VERBOSE_QUEUE_FULL)
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+              if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
               {
                 if (queue_flag)
                   printk(INFO_LEAD "Queue full received; queue depth %d, "
                     "active %d\n", p->host_no, CTL_OF_SCB(scb),
-                    p->dev_max_queue_depth[scratch_offset],
-                    p->dev_active_cmds[scratch_offset]);
+                    p->dev_max_queue_depth[tindex],
+                    p->dev_active_cmds[tindex]);
                 else
                   printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
 
               }
+#endif
               if (queue_flag)
               {
-                p->dev_temp_queue_depth[scratch_offset] = 
-                  p->dev_active_cmds[scratch_offset];
-                if ( p->dev_last_queue_full[scratch_offset] !=
-                     p->dev_active_cmds[scratch_offset] )
+                p->dev_temp_queue_depth[tindex] = 
+                  p->dev_active_cmds[tindex];
+                if ( p->dev_last_queue_full[tindex] !=
+                     p->dev_active_cmds[tindex] )
                 {
-                  p->dev_last_queue_full[scratch_offset] = 
-                      p->dev_active_cmds[scratch_offset];
-                  p->dev_last_queue_full_count[scratch_offset] = 0;
+                  p->dev_last_queue_full[tindex] = 
+                      p->dev_active_cmds[tindex];
+                  p->dev_last_queue_full_count[tindex] = 0;
                 }
                 else
                 {
-                  p->dev_last_queue_full_count[scratch_offset]++;
+                  p->dev_last_queue_full_count[tindex]++;
                 }
-                if ( (p->dev_last_queue_full_count[scratch_offset] > 14) &&
-                     (p->dev_active_cmds[scratch_offset] > 4) )
+                if ( (p->dev_last_queue_full_count[tindex] > 14) &&
+                     (p->dev_active_cmds[tindex] > 4) )
                 {
-                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
+                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
                     printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
-                      CTL_OF_SCB(scb), p->dev_active_cmds[scratch_offset]);
-                  p->dev_max_queue_depth[scratch_offset] = 
-                      p->dev_active_cmds[scratch_offset];
-                  p->dev_last_queue_full[scratch_offset] = 0;
-                  p->dev_last_queue_full_count[scratch_offset] = 0;
+                      CTL_OF_SCB(scb), p->dev_active_cmds[tindex]);
+                  p->dev_max_queue_depth[tindex] = 
+                      p->dev_active_cmds[tindex];
+                  p->dev_last_queue_full[tindex] = 0;
+                  p->dev_last_queue_full_count[tindex] = 0;
                 }
               }
               break;
@@ -4166,6 +4981,15 @@
          * this target.
          */
 
+        if ( !(scb->flags & SCB_DEVICE_RESET) &&
+              (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG) &&
+              (scb->hscb->control & TAG_ENB) )
+        {
+          p->msg_buf[p->msg_index++] = scb->tag_action;
+          p->msg_buf[p->msg_index++] = scb->hscb->tag;
+          p->msg_len += 2;
+        }
+
         if (scb->flags & SCB_DEVICE_RESET)
         {
           p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
@@ -4178,12 +5002,6 @@
         {
           if (scb->tag_action)
           {
-            if (msg_out == MSG_IDENTIFYFLAG)
-            {
-              p->msg_buf[p->msg_index++] = scb->tag_action;
-              p->msg_buf[p->msg_index++] = scb->hscb->tag;
-	      p->msg_len += 2;
-	    }
             p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
           }
           else
@@ -4197,23 +5015,67 @@
         }
         else if (scb->flags & SCB_MSGOUT_WDTR)
         {
-          aic7xxx_construct_wdtr(p, (scb->flags & SCB_WDTR_16BIT));
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+          if (aic7xxx_verbose > 0xffff)
+            printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
+                   CTL_OF_SCB(scb));
+#endif
+          aic7xxx_construct_wdtr(p,
+            p->transinfo[TARGET_INDEX(scb->cmd)].goal_width);
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
-          unsigned char period, offset;
-
+          unsigned int max_sync, period;
           /*
-           * Pull the user defined setting from scratch RAM.
+           * We need to set an accurate goal_offset instead of
+           * the ridiculously high one we default to.  We should
+           * now know if we are wide.  Plus, the WDTR code will 
+           * set our goal_offset for us as well.
            */
-          period = p->syncinfo[scratch_offset].period;
-          offset = p->syncinfo[scratch_offset].offset;
-          if ( (p->needsdtr_copy & target_mask) == 0)
+          if (p->transinfo[tindex].goal_offset)
+          {
+            if (p->features & AHC_ULTRA2)
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+            else if (p->transinfo[tindex].cur_width == MSG_EXT_WDTR_BUS_16_BIT)
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+            else
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+          }
+          /*
+           * Now that the device is selected, use the bits in SBLKCTL and
+           * SSTAT2 to determine the max sync rate for this device.
+           */
+          if (p->features & AHC_ULTRA2)
+          {
+            if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+                !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+            {
+              max_sync = AHC_SYNCRATE_ULTRA2;
+            }
+            else
+            {
+              max_sync = AHC_SYNCRATE_ULTRA;
+            }
+          }
+          else if (p->features & AHC_ULTRA)
           {
-            period = 0;
-            offset = 0;
+            max_sync = AHC_SYNCRATE_ULTRA;
           }
-          aic7xxx_construct_sdtr(p, period, offset);
+          else
+          {
+            max_sync = AHC_SYNCRATE_FAST;
+          }
+          period = p->transinfo[tindex].goal_period;
+          aic7xxx_find_syncrate(p, &period, max_sync);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+          if (aic7xxx_verbose > 0xffff)
+            printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
+                   CTL_OF_SCB(scb),
+                   p->transinfo[tindex].goal_period,
+                   p->transinfo[tindex].goal_offset);
+#endif
+          aic7xxx_construct_sdtr(p, period,
+            p->transinfo[tindex].goal_offset);
         }
         else 
         {
@@ -4233,7 +5095,7 @@
          * have this problem since they continually interrupt the kernel
          * until we take care of the situation.
          */
-        aic_outb(p, CLRSEQINT, CLRINT);
+        scb->flags |= SCB_MSGOUT_SENT;
         p->msg_index = 0;
         p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
         p->flags |= AHC_HANDLING_REQINITS;
@@ -4282,6 +5144,7 @@
       }
       break;
 
+#if AIC7XXX_NOT_YET 
     case TRACEPOINT:
       {
         printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel,
@@ -4296,7 +5159,6 @@
       }
       break;
 
-#if AIC7XXX_NOT_YET 
     /* XXX Fill these in later */
     case MSG_BUFFER_BUSY:
       printk("aic7xxx: Message buffer busy.\n");
@@ -4316,7 +5178,6 @@
   /*
    * Clear the sequencer interrupt and unpause the sequencer.
    */
-  aic_outb(p, CLRSEQINT, CLRINT);
   unpause_sequencer(p, /* unpause always */ TRUE);
 }
 
@@ -4331,14 +5192,18 @@
 static int
 aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  int reject, done;
-  unsigned char target_scratch, scratch_offset;
+  int reject, reply, done;
+  unsigned char target_scsirate, tindex;
   unsigned short target_mask;
+  unsigned char target, channel, lun;
 
-  reject = done = FALSE;
-  scratch_offset = TARGET_INDEX(scb->cmd);
-  target_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
-  target_mask = (0x01 << scratch_offset);
+  target = scb->cmd->target;
+  channel = scb->cmd->channel;
+  lun = scb->cmd->lun;
+  reply = reject = done = FALSE;
+  tindex = TARGET_INDEX(scb->cmd);
+  target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+  target_mask = (0x01 << tindex);
 
   /*
    * Parse as much of the message as is availible,
@@ -4364,9 +5229,10 @@
     {
       case MSG_EXT_SDTR:
       {
-        unsigned char period, response_period, offset;
-        unsigned char max_offset, saved_offset, rate;
-
+        unsigned int period, offset;
+        unsigned char maxsync, saved_offset;
+        struct aic7xxx_syncrate *syncrate;
+        
         if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
         {
           reject = TRUE;
@@ -4379,51 +5245,114 @@
         }
 
         period = p->msg_buf[3];
-        saved_offset = p->msg_buf[4];
+        saved_offset = offset = p->msg_buf[4];
 
-        if (target_scratch & WIDEXFER)
+        if (p->features & AHC_ULTRA2)
+        {
+          if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+               !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+          {
+            maxsync = AHC_SYNCRATE_ULTRA2;
+          }
+          else
+          {
+            maxsync = AHC_SYNCRATE_ULTRA;
+          }
+        }
+        else if (p->features & AHC_ULTRA)
         {
-          max_offset = MAX_OFFSET_16BIT;
+          maxsync = AHC_SYNCRATE_ULTRA;
         }
         else
         {
-          max_offset = MAX_OFFSET_8BIT;
+          maxsync = AHC_SYNCRATE_FAST;
         }
-        offset = MIN(saved_offset, max_offset);
-        response_period = aic7xxx_scsirate(p, &rate, &period,
-          &offset, scb->cmd->target, scb->cmd->channel, /* set */ TRUE);
-        /* Preserve the WideXfer flag */
-        target_scratch = rate | (target_scratch & WIDEXFER);
-
-        /*
-         * Update the TARGET_SCRATCH, the SCSIRATE, and our syncinfo
-         * areas.
-         */
-        aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
-        aic_outb(p, target_scratch, SCSIRATE);
-        p->syncinfo[scratch_offset].period = response_period;
-        p->syncinfo[scratch_offset].offset = offset;
+  
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if (aic7xxx_verbose > 0xffff)
+        {
+          printk(INFO_LEAD "Finished receipt of SDTR, parsing %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+          syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+          printk(INFO_LEAD "After find_syncrate() %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+          aic7xxx_validate_offset(p, syncrate, &offset,
+                                  target_scsirate & WIDEXFER);
+          printk(INFO_LEAD "After validate_offset() %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                               offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+          printk(INFO_LEAD "Final values of Period/Offset as set: %d/%d\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset);
+        }
+        else
+        {
+          syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+          aic7xxx_validate_offset(p, syncrate, &offset,
+                                  target_scsirate & WIDEXFER);
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                               offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        }
+#else
+        syncrate = aic7xxx_find_syncrate(p, &period, maxsync);
+        aic7xxx_validate_offset(p, syncrate, &offset,
+                                target_scsirate & WIDEXFER);
+        aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                             offset, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+#endif
 
+        if (offset == 0)
+        {
+          /*
+           * Uhh ohh, things fell through to async....update the goal
+           * items and the needsdtr_copy to reflect this...
+           */
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                 offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+          p->needsdtr_copy &= ~target_mask;
+        }
         /*
          * Did we start this, if not, or if we went to low and had to
          * go async, then send an SDTR back to the target
          */
         p->needsdtr &= ~target_mask;
-        if (scb->flags & SCB_MSGOUT_SDTR)
+        p->sdtr_pending &= ~target_mask;
+        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ==
+              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) &&
+             (offset == saved_offset) )
         {
-          if (saved_offset != offset)
-          {
-            p->needsdtr_copy &= ~target_mask;
-            reject = TRUE; 
-          }
-          scb->flags &= ~SCB_MSGOUT_SDTR;
-          p->sdtr_pending &= ~target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
         }
         else
         {
+          /*
+           * Send a reply SDTR back.  Even if we sent the first one, it
+           * is valid to send another one out immediately to re-negotiate
+           * things, and a few devices don't like getting rejects after
+           * we already sent them one SDTR.  Just send an SDTR for async
+           * this time if need be (or for the correct params if we didn't
+           * start all of this).  If this is a Reject Reply type message,
+           * then we've put the async settings into the goal area for
+           * future reference (when we get the AWAITING_MSG interrupt).
+           * If this is a case where we are responding to the target's
+           * initiated SDTR, then leave our own goal and user values in
+           * place (unless the device hasn't been scanned yet, in which
+           * case, put the user values into the goal values so we don't
+           * send out an Async message).
+           */
+          if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
+          {
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            p->transinfo[tindex].goal_offset =
+              p->transinfo[tindex].user_offset;
+            p->needwdtr_copy |= target_mask;
+            p->needsdtr_copy |= target_mask;
+          }
           scb->flags &= ~SCB_MSGOUT_BITS;
           scb->flags |= SCB_MSGOUT_SDTR;
-          p->sdtr_pending |= target_mask;
           aic_outb(p, HOST_MSG, MSG_OUT);
           aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
         }
@@ -4446,112 +5375,111 @@
         }
 
         bus_width = p->msg_buf[3];
-        p->needwdtr &= ~target_mask;
-        if (scb->flags & SCB_MSGOUT_WDTR)
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
         {
           switch(bus_width)
           {
             default:
             {
               reject = TRUE;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_WDTR) ||
+                    (aic7xxx_verbose > 0xffff)) )
               {
                 printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
                   p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+                p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
               }
             } /* We fall through on purpose */
             case MSG_EXT_WDTR_BUS_8_BIT:
             {
               bus_width = MSG_EXT_WDTR_BUS_8_BIT;
               p->needwdtr_copy &= ~target_mask;
-              target_scratch &= 0x7f;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-              {
-                printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
-                  p->host_no, CTL_OF_SCB(scb));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-              }
               break;
             }
             case MSG_EXT_WDTR_BUS_16_BIT:
             {
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-              {
-                printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
-                  p->host_no, CTL_OF_SCB(scb));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-              }
-              target_scratch |= WIDEXFER;
               break;
             }
           }
-          scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+          scb->flags &= ~SCB_MSGOUT_BITS;
           p->wdtr_pending &= ~target_mask;
-          /*
-           * By virtue of the SCSI spec, a WDTR message negates any existing
-           * SDTR negotiations.  So, even if needsdtr isn't marked for this
-           * device, we still have to do a new SDTR message if the device
-           * supports SDTR at all.  Therefore, we check needsdtr_copy instead
-           * of needstr.
-           */
-          if ( (p->needsdtr_copy & target_mask) &&
-              !(p->sdtr_pending & target_mask))
-          {
-            p->needsdtr |= target_mask;
-          }
+          p->needwdtr &= ~target_mask;
         }
         else
         {
           scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_WDTR;
+          reply = TRUE;
+          if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
+          {
+            /* 
+             * Well, we now know the WDTR and SYNC caps of this device since
+             * it contacted us first, mark it as such and copy the user stuff
+             * over to the goal stuff.
+             */
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            p->transinfo[tindex].goal_offset =
+              p->transinfo[tindex].user_offset;
+            p->needwdtr_copy |= target_mask;
+            p->needsdtr_copy |= target_mask;
+          }
           switch(bus_width)
           {
             default:
             {
-              if (p->type & AHC_WIDE)
+              if ( (p->features & AHC_WIDE) &&
+                   (p->transinfo[tindex].goal_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) )
               {
                 bus_width = MSG_EXT_WDTR_BUS_16_BIT;
-                p->needwdtr_copy |= target_mask;
-                scb->flags |= SCB_MSGOUT_WDTR_16BIT;
-                target_scratch |= WIDEXFER;
-                if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                     (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-                {
-                  printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
-                    p->host_no, CTL_OF_SCB(scb));
-                  p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-                }
                 break;
               }
             } /* Fall through if we aren't a wide card */
             case MSG_EXT_WDTR_BUS_8_BIT:
             {
-              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
               p->needwdtr_copy &= ~target_mask;
-              scb->flags |= SCB_MSGOUT_WDTR_8BIT;
-              target_scratch &= 0x7f;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
-              {
-                printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
-                  p->host_no, CTL_OF_SCB(scb));
-                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
-              }
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
               break;
             }
           }
+          p->needwdtr &= ~target_mask;
+          p->wdtr_pending &= ~target_mask;
           aic_outb(p, HOST_MSG, MSG_OUT);
           aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
-          p->wdtr_pending |= target_mask;
         }
-        aic_outb(p, target_scratch, SCSIRATE);
-        aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
-        p->syncinfo[scratch_offset].offset =
-          (bus_width == MSG_EXT_WDTR_BUS_8_BIT) ? 
-          MAX_OFFSET_8BIT : MAX_OFFSET_16BIT;
+        aic7xxx_set_width(p, target, channel, lun, bus_width,
+                          AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        
+        /*
+         * By virtue of the SCSI spec, a WDTR message negates any existing
+         * SDTR negotiations.  So, even if needsdtr isn't marked for this
+         * device, we still have to do a new SDTR message if the device
+         * supports SDTR at all.  Therefore, we check needsdtr_copy instead
+         * of needstr.
+         */
+        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0,
+                             AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+        if ( (p->needsdtr_copy & target_mask) &&
+            !(p->sdtr_pending & target_mask))
+        {
+          p->needsdtr |= target_mask;
+          if ( !reject && !reply )
+          {
+            scb->flags &= ~SCB_MSGOUT_WDTR;
+            if (p->transinfo[tindex].goal_period)
+            {
+              p->sdtr_pending |= target_mask;
+              scb->flags |= SCB_MSGOUT_SDTR;
+              aic_outb(p, HOST_MSG, MSG_OUT);
+              aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+            }
+          }
+        }
         done = TRUE;
         break;
       }
@@ -4567,10 +5495,12 @@
   {
     aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
     aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+    done = TRUE;
   }
   return(done);
 }
 
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_handle_reqinit
@@ -4584,7 +5514,7 @@
 {
   unsigned char lastbyte;
   unsigned char phasemis;
-  int done;
+  int done = FALSE;
 
   switch(p->msg_type)
   {
@@ -4614,10 +5544,20 @@
           {
             aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
             aic_outb(p, 0, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+            if (aic7xxx_verbose > 0xffff)
+              printk(INFO_LEAD "Completed sending of REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+#endif
           }
           else
           {
             aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+            if (aic7xxx_verbose > 0xffff)
+              printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+#endif
           }
           unpause_sequencer(p, TRUE);
         }
@@ -4651,12 +5591,23 @@
         }
         if (phasemis || done)
         {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+          if (aic7xxx_verbose > 0xffff)
+          {
+            if (phasemis)
+              printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+            else
+              printk(INFO_LEAD "Completed receipt of REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+          }
+#endif
           /* Time to end our message session */
           p->msg_len = 0;
           p->msg_type = MSG_TYPE_NONE;
-          p->flags &= ~AHC_HANDLING_REQINITS;
           aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
           aic_outb(p, CLRSCSIINT, CLRINT);
+          p->flags &= ~AHC_HANDLING_REQINITS;
           unpause_sequencer(p, TRUE);
         }
         break;
@@ -4700,30 +5651,14 @@
   }
 
 
-  if ( (p->flags & AHC_HANDLING_REQINITS) && (status & REQINIT) )
-  {
-    if (scb)
-    {
-      aic7xxx_handle_reqinit(p, scb);
-    }
-    else
-    {
-      p->flags &= ~AHC_HANDLING_REQINITS;
-      aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
-      aic_outb(p, CLRREQINIT, CLRSINT1);
-      aic_outb(p, CLRSCSIINT, CLRINT);
-      p->msg_type = MSG_TYPE_NONE;
-      p->msg_index = 0;
-      p->msg_len = 0;
-    }
-    return;
-  }
-
   if ((status & SCSIRSTI) != 0)
   {
     int channel;
 
-    channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    else
+      channel = 0;
 
     if (aic7xxx_verbose & VERBOSE_RESET)
       printk(WARN_LEAD "Someone else reset the channel!!\n",
@@ -4746,10 +5681,16 @@
     unsigned char lastphase = aic_inb(p, LASTPHASE);
     unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
     unsigned char target = (saved_tcl >> 4) & 0x0F;
-    int channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    int channel;
     int printerror = TRUE;
 
-    aic_outb(p, 0, SCSISEQ);
+    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    else
+      channel = 0;
+
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+             SCSISEQ);
     if (lastphase == P_MESGOUT)
     {
       unsigned char message;
@@ -4791,20 +5732,16 @@
         aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
         aic7xxx_run_done_queue(p, FALSE);
       }
-      else
-      {  /* Since we don't really know what happened here, we'll wait */
-         /* for the commands to timeout and get aborted if need be    */
-        aic7xxx_add_curscb_to_free_list(p);
-      }
       printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
              "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
              (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
       scb = NULL;
     }
+    aic_outb(p, MSG_NOOP, MSG_OUT);
     aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
       SIMODE1);
     p->flags &= ~AHC_HANDLING_REQINITS;
-    aic_outb(p, CLRBUSFREE | CLRREQINIT, CLRSINT1);
+    aic_outb(p, CLRBUSFREE, CLRSINT1);
     aic_outb(p, CLRSCSIINT, CLRINT);
     restart_sequencer(p);
     unpause_sequencer(p, TRUE);
@@ -4816,17 +5753,26 @@
     Scsi_Cmnd *cmd;
 
     scbptr = aic_inb(p, WAITING_SCBH);
-    if (scbptr >= p->scb_data->maxhscbs)
-    {
-      scb_index = SCB_LIST_NULL;
-      printk(WARN_LEAD "Bad scbptr %d during SELTO.\n", 
-        p->host_no, -1, -1, -1, scbptr);
-    }
-    else
+    if (scbptr > p->scb_data->maxhscbs)
     {
-      aic_outb(p, scbptr, SCBPTR);
-      scb_index = aic_inb(p, SCB_TAG);
+      /*
+       * I'm still trying to track down exactly how this happens, but until
+       * I find it, this code will make sure we aren't passing bogus values
+       * into the SCBPTR register, even if that register will just wrap
+       * things around, we still don't like having out of range variables.
+       *
+       * NOTE: Don't check the aic7xxx_verbose variable, I want this message
+       * to always be displayed.
+       */
+      printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n",
+             p->host_no, -1, -1, -1, scbptr);
+      if (p->scb_data->maxhscbs > 4)
+        scbptr &= (p->scb_data->maxhscbs - 1);
+      else
+        scbptr &= 0x03;
     }
+    aic_outb(p, scbptr, SCBPTR);
+    scb_index = aic_inb(p, SCB_TAG);
 
     scb = NULL;
     if (scb_index < p->scb_data->numscbs)
@@ -4845,6 +5791,8 @@
              "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
              aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
              aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+      if (aic7xxx_panic_on_abort)
+        aic7xxx_panic_abort(p, NULL);
     }
     else
     {
@@ -4881,6 +5829,10 @@
        * What we need to do then is to let the command timeout again so
        * we get a reset since this abort just failed.
        */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
+#endif
       if (p->flags & SCB_QUEUED_ABORT)
       {
         cmd->result = 0;
@@ -4892,9 +5844,10 @@
      * Restarting the sequencer will stop the selection and make sure devices
      * are allowed to reselect in.
      */
-    aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+    aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
     p->flags &= ~AHC_HANDLING_REQINITS;
-    aic_outb(p, CLRSELTIMEO | CLRBUSFREE | CLRREQINIT, CLRSINT1);
+    aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
     aic_outb(p, CLRSCSIINT, CLRINT);
     restart_sequencer(p);
     unpause_sequencer(p, TRUE);
@@ -4959,7 +5912,7 @@
      * A parity error has occurred during a data
      * transfer phase. Flag it and continue.
      */
-    printk(WARN_LEAD "Parity error during phase %s.\n",
+    printk(WARN_LEAD "Parity error during %s phase.\n",
            p->host_no, CTL_OF_SCB(scb), phase);
 
     /*
@@ -4977,6 +5930,17 @@
     aic_outb(p, CLRSCSIINT, CLRINT);
     unpause_sequencer(p, /* unpause_always */ TRUE);
   }
+  else if ( (status & REQINIT) &&
+            (p->flags & AHC_HANDLING_REQINITS) )
+  {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (aic7xxx_verbose > 0xffff)
+      printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no,
+             CTL_OF_SCB(scb), aic_inb(p, SSTAT1));
+#endif
+    aic7xxx_handle_reqinit(p, scb);
+    return;
+  }
   else
   {
     /*
@@ -4997,83 +5961,168 @@
   }
 }
 
-#ifdef CONFIG_PCI
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void
+aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
+{
+  unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp;
+  int i, bogus, lost;
+  static unsigned char scb_status[AIC7XXX_MAXSCB];
+
+#define SCB_NO_LIST 0
+#define SCB_FREE_LIST 1
+#define SCB_WAITING_LIST 2
+#define SCB_DISCONNECTED_LIST 4
+#define SCB_CURRENTLY_ACTIVE 8
+
+  /*
+   * Note, these checks will fail on a regular basis once the machine moves
+   * beyond the bus scan phase.  The problem is race conditions concerning
+   * the scbs and where they are linked in.  When you have 30 or so commands
+   * outstanding on the bus, and run this twice with every interrupt, the
+   * chances get pretty good that you'll catch the sequencer with an SCB
+   * only partially linked in.  Therefore, once we pass the scan phase
+   * of the bus, we really should disable this function.
+   */
+  bogus = FALSE;
+  memset(&scb_status[0], 0, sizeof(scb_status));
+  pause_sequencer(p);
+  saved_scbptr = aic_inb(p, SCBPTR);
+  if (saved_scbptr >= p->scb_data->maxhscbs)
+  {
+    printk("Bogus SCBPTR %d\n", saved_scbptr);
+    bogus = TRUE;
+  }
+  scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE;
+  free_scbh = aic_inb(p, FREE_SCBH);
+  if ( (free_scbh != SCB_LIST_NULL) &&
+       (free_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus FREE_SCBH %d\n", free_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = free_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_FREE_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_FREE_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
 
-#define  DPE 0x80
-#define  SSE 0x40
-#define  RMA 0x20
-#define  RTA 0x10
-#define  STA 0x08
-#define  DPR 0x01
+  dis_scbh = aic_inb(p, DISCONNECTED_SCBH);
+  if ( (dis_scbh != SCB_LIST_NULL) &&
+       (dis_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = dis_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_DISCONNECTED_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_DISCONNECTED_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
+  
+  wait_scbh = aic_inb(p, WAITING_SCBH);
+  if ( (wait_scbh != SCB_LIST_NULL) &&
+       (wait_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus WAITING_SCBH %d\n", wait_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = wait_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_WAITING_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_WAITING_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
+
+  lost=0;
+  for(i=0; i < p->scb_data->maxhscbs; i++)
+  {
+    aic_outb(p, i, SCBPTR);
+    temp = aic_inb(p, SCB_NEXT);
+    if ( ((temp != SCB_LIST_NULL) &&
+          (temp >= p->scb_data->maxhscbs)) )
+    {
+      printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp);
+      bogus = TRUE;
+    }
+    if ( temp == i )
+    {
+      printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
+      bogus = TRUE;
+    }
+    temp = aic_inb(p, SCB_PREV);
+    if ((temp != SCB_LIST_NULL) &&
+        (temp >= p->scb_data->maxhscbs))
+    {
+      printk("HSCB %d bad, SCB_PREV invalid(%d).\n", i, temp);
+      bogus = TRUE;
+    }
+    if (scb_status[i] == 0)
+      lost++;
+    if (lost > 1)
+    {
+      printk("Too many lost scbs.\n");
+      bogus=TRUE;
+    }
+  }
+  aic_outb(p, saved_scbptr, SCBPTR);
+  unpause_sequencer(p, FALSE);
+  if (bogus)
+  {
+    printk("Bogus parameters found in card SCB array structures.\n");
+    printk("%s\n", buffer);
+    aic7xxx_panic_abort(p, NULL);
+  }
+  return;
+}
+#endif
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_pci_intr
+ *   aic7xxx_isr
  *
  * Description:
- *   Check the scsi card for PCI errors and clear the interrupt
- *
- *   NOTE: If you don't have this function and a 2940 card encounters
- *         a PCI error condition, the machine will end up locked as the
- *         interrupt handler gets slammed with non-stop PCI error interrupts
+ *   SCSI controller interrupt handler.
  *-F*************************************************************************/
 static void
-aic7xxx_pci_intr(struct aic7xxx_host *p)
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
 {
-  unsigned char status1;
+  struct aic7xxx_host *p;
+  unsigned char intstat;
 
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
-  pci_read_config_byte(p->pdev, PCI_STATUS, &status1);
-#else
-  pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
-                                            PCI_STATUS, &status1);
-#endif
-
-  if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
-      "phase.\n", p->host_no, -1, -1, -1);
-  if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
-      "PERR#\n", p->host_no, -1, -1, -1);
-  
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
-  pci_write_config_byte(p->pdev, PCI_STATUS, status1);
-#else
-  pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
-                                            PCI_STATUS, status1);
-#endif
-  if (status1 & (DPR|RMA|RTA))
-    aic_outb(p,  CLRPARERR, CLRINT);
-
-}
-#endif
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_isr
- *
- * Description:
- *   SCSI controller interrupt handler.
- *-F*************************************************************************/
-static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
-{
-  struct aic7xxx_host *p;
-  unsigned char intstat;
-
-  p = (struct aic7xxx_host *)dev_id;
+  p = (struct aic7xxx_host *)dev_id;
 
   /*
    * Just a few sanity checks.  Make sure that we have an int pending.
@@ -5083,7 +6132,8 @@
   if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
   {
 #ifdef CONFIG_PCI
-    if ((p->type & AHC_AIC78x0) && (p->spurious_int > 500))
+    if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) &&
+        !(p->flags & AHC_HANDLING_REQINITS) )
     {
       if ( aic_inb(p, ERROR) & PCIERRSTAT )
       {
@@ -5091,7 +6141,7 @@
       }
       p->spurious_int = 0;
     }
-    else
+    else if ( !(p->flags & AHC_HANDLING_REQINITS) )
     {
       p->spurious_int++;
     }
@@ -5106,6 +6156,12 @@
    */
   p->isr_count++;
 
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+    aic7xxx_check_scbs(p, "Bogus settings at start of interrupt.");
+#endif
+
   /*
    * Handle all the interrupt sources - especially for SCSI
    * interrupts, we won't get a second chance at them.
@@ -5116,6 +6172,11 @@
     Scsi_Cmnd *cmd;
     unsigned char scb_index;
 
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if(aic7xxx_verbose > 0xffff)
+      printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
+#endif
+    
     /*
      * Clear interrupt status before running the completion loop.
      * This eliminates a race condition whereby a command could
@@ -5169,6 +6230,14 @@
           SCB_QUEUED_ABORT);
         unpause_sequencer(p, FALSE);
       }
+      else if (scb->flags & SCB_ABORT)
+      {
+        /*
+         * We started to abort this, but it completed on us, let it
+         * through as successful
+         */
+        scb->flags &= ~(SCB_ABORT|SCB_RESET);
+      }
       switch (status_byte(scb->hscb->target_status))
       {
         case QUEUE_FULL:
@@ -5209,16 +6278,35 @@
         printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
       }
     }
-    printk(KERN_ERR "(scsi%d)   LINE=%d\n", p->host_no,
+    printk(KERN_ERR "(scsi%d)   SEQADDR=0x%x\n", p->host_no,
       (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
-    aic7xxx_reset_channel(p, 0, TRUE);
-    if ( p->type & AHC_TWIN )
+    if (aic7xxx_panic_on_abort)
+      aic7xxx_panic_abort(p, NULL);
+#ifdef CONFIG_PCI
+    if (errno & PCIERRSTAT)
+      aic7xxx_pci_intr(p);
+#endif
+    if (errno & (SQPARERR | ILLOPCODE | ILLSADDR))
     {
-      aic7xxx_reset_channel(p, 1, TRUE);
-      restart_sequencer(p);
+      sti();
+      panic("aic7xxx: unrecoverable BRKADRINT.\n");
     }
-    aic7xxx_run_done_queue(p, FALSE);
-    aic_outb(p, CLRBRKADRINT, CLRINT);
+    if (errno & ILLHADDR)
+    {
+      printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first "
+             "pausing controller!\n", p->host_no);
+    }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (errno & DPARERR)
+    {
+      if (aic_inb(p, DMAPARAMS) & DIRECTION)
+        printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no);
+      else
+        printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no);
+    }
+#endif
+    aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT);
+    unpause_sequencer(p, FALSE);
   }
 
   if (intstat & SEQINT)
@@ -5230,6 +6318,13 @@
   {
     aic7xxx_handle_scsiint(p, intstat);
   }
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+    aic7xxx_check_scbs(p, "Bogus settings at end of interrupt.");
+#endif
+
 }
 
 /*+F*************************************************************************
@@ -5246,49 +6341,34 @@
 {
   unsigned long cpu_flags;
   struct aic7xxx_host *p;
-  static unsigned int re_entry_counter = 0;
   
   p = (struct aic7xxx_host *)dev_id;
   if(!p)
     return;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95)
+  spin_lock_irqsave(&io_request_lock, cpu_flags);
   if(test_and_set_bit(AHC_IN_ISR_BIT, &p->flags))
   {
-    if(re_entry_counter++ > 100000UL)
-    {
-      /*
-       * Hmmm...we seem to be looping here.  This usually means that our
-       * interrupt routine got killed by a NULL pointer deref.  Panic.
-       */
-      sti();
-      panic("aic7xxx: The interrupt routine appears to have seg faulted.\n");
-    }
     return;
   }
-  re_entry_counter = 0;
-  spin_lock_irqsave(&io_request_lock, cpu_flags);
-  aic7xxx_isr(irq, dev_id, regs);
+  do
+  {
+    aic7xxx_isr(irq, dev_id, regs);
+  } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
   aic7xxx_done_cmds_complete(p);
   aic7xxx_run_waiting_queues(p);
-  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
   clear_bit(AHC_IN_ISR_BIT, &p->flags);
+  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
 #else
   if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags))
   {
-    if(re_entry_counter++ > 100000UL)
-    {
-      /*
-       * Hmmm...we seem to be looping here.  This usually means that our
-       * interrupt routine got killed by a NULL pointer deref.  Panic.
-       */
-      sti();
-      panic("aic7xxx: The interrupt routine appears to have seg faulted.\n");
-    }
     return;
   }
-  re_entry_counter = 0;
   DRIVER_LOCK
-  aic7xxx_isr(irq, dev_id, regs);
+  do
+  {
+    aic7xxx_isr(irq, dev_id, regs);
+  } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
   DRIVER_UNLOCK
   aic7xxx_done_cmds_complete(p);
   aic7xxx_run_waiting_queues(p);
@@ -5341,7 +6421,7 @@
  
     if (!(p->discenable & target_mask))
     {
-      if (aic7xxx_verbose & VERBOSE_QUEUE)
+      if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
         printk(INFO_LEAD "Disconnection disabled, unable to "
              "enable tagged queueing.\n",
              p->host_no, device->channel, device->id, device->lun);
@@ -5381,7 +6461,7 @@
       }
       if ((device->tagged_queue == 0) && tag_enabled)
       {
-        if (aic7xxx_verbose & VERBOSE_QUEUE)
+        if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
         {
               printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
                 p->host_no, device->channel, device->id,
@@ -5435,7 +6515,7 @@
      * to allocate some when memory is more or less exhausted and we need
      * the SCB in order to perform a swap operation (possible deadlock)
      */
-    if ( aic7xxx_allocate_scb(p, TRUE) == NULL )
+    if ( aic7xxx_allocate_scb(p) == 0 )
       return;
   }
 }
@@ -5468,7 +6548,7 @@
  *   an unused function.
  *-F*************************************************************************/
 #if defined(__i386__) || defined(__alpha__)
-static ahc_type
+static int
 aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
 {
   int i;
@@ -5477,13 +6557,17 @@
   static struct {
     int n;
     unsigned char signature[sizeof(buf)];
-    ahc_type type;
+    ahc_chip type;
     int bios_disabled;
   } AIC7xxx[] = {
-    { 4, { 0x04, 0x90, 0x77, 0x71 }, AHC_274, FALSE }, /* host adapter 274x */
-    { 4, { 0x04, 0x90, 0x77, 0x70 }, AHC_AIC7770, FALSE },  /* mb 7770  */
-    { 4, { 0x04, 0x90, 0x77, 0x56 }, AHC_284, FALSE }, /* 284x BIOS enabled */
-    { 4, { 0x04, 0x90, 0x77, 0x57 }, AHC_284, TRUE }   /* 284x BIOS disabled */
+    { 4, { 0x04, 0x90, 0x77, 0x70 },
+      AHC_AIC7770|AHC_EISA, FALSE },  /* mb 7770  */
+    { 4, { 0x04, 0x90, 0x77, 0x71 },
+      AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */
+    { 4, { 0x04, 0x90, 0x77, 0x56 },
+      AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */
+    { 4, { 0x04, 0x90, 0x77, 0x57 },
+      AHC_AIC7770|AHC_VL, TRUE }   /* 284x BIOS disabled */
   };
 
   /*
@@ -5513,7 +6597,7 @@
         {
           *flags |= AHC_BIOS_ENABLED;
         }
-        return (AIC7xxx[i].type);
+        return (i);
       }
 
       printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
@@ -5521,7 +6605,7 @@
     }
   }
 
-  return (AHC_NONE);
+  return (-1);
 }
 #endif /* (__i386__) || (__alpha__) */
 
@@ -5678,7 +6762,7 @@
  * Description:
  *   Acquires access to the memory port on PCI controllers.
  *-F*************************************************************************/
-static inline int
+static int
 acquire_seeprom(struct aic7xxx_host *p)
 {
   int wait;
@@ -5695,7 +6779,7 @@
   while ((wait > 0) && ((aic_inb(p, SEECTL) & SEERDY) == 0))
   {
     wait--;
-    udelay(1000);  /* 1 msec */
+    mdelay(1);  /* 1 msec */
   }
   if ((aic_inb(p, SEECTL) & SEERDY) == 0)
   {
@@ -5712,7 +6796,7 @@
  * Description:
  *   Releases access to the memory port on PCI controllers.
  *-F*************************************************************************/
-static inline void
+static void
 release_seeprom(struct aic7xxx_host *p)
 {
   aic_outb(p, 0, SEECTL);
@@ -5912,19 +6996,38 @@
  * Description:
  *   Writes a value to the BRDCTL register.
  *-F*************************************************************************/
-static inline void
+static void
 write_brdctl(struct aic7xxx_host *p, unsigned char value)
 {
   unsigned char brdctl;
 
-  brdctl = BRDCS | BRDSTB;
+  if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+  {
+    brdctl = BRDSTB;
+    if (p->flags & AHC_CHNLB)
+      brdctl |= BRDCS;
+  }
+  else if (p->features & AHC_ULTRA2)
+    brdctl = 0;
+  else
+    brdctl = BRDSTB | BRDCS;
   aic_outb(p, brdctl, BRDCTL);
+  udelay(1);
   brdctl |= value;
   aic_outb(p, brdctl, BRDCTL);
-  brdctl &= ~BRDSTB;
+  udelay(1);
+  if (p->features & AHC_ULTRA2)
+    brdctl |= BRDSTB_ULTRA2;
+  else
+    brdctl &= ~BRDSTB;
   aic_outb(p, brdctl, BRDCTL);
-  brdctl &= ~BRDCS;
+  udelay(1);
+  if (p->features & AHC_ULTRA2)
+    brdctl = 0;
+  else
+    brdctl &= ~BRDCS;
   aic_outb(p, brdctl, BRDCTL);
+  udelay(1);
 }
 
 /*+F*************************************************************************
@@ -5934,11 +7037,27 @@
  * Description:
  *   Reads the BRDCTL register.
  *-F*************************************************************************/
-static inline unsigned char
+static unsigned char
 read_brdctl(struct aic7xxx_host *p)
 {
-  aic_outb(p, BRDRW | BRDCS, BRDCTL);
-  return (aic_inb(p, BRDCTL));
+  unsigned char brdctl, value;
+
+  if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+  {
+    brdctl = BRDRW;
+    if (p->flags & AHC_CHNLB)
+      brdctl |= BRDCS;
+  }
+  else if (p->features & AHC_ULTRA2)
+    brdctl = BRDRW_ULTRA2;
+  else
+    brdctl = BRDRW | BRDCS;
+  aic_outb(p, brdctl, BRDCTL);
+  udelay(1);
+  value = aic_inb(p, BRDCTL);
+  aic_outb(p, 0, BRDCTL);
+  udelay(1);
+  return (value);
 }
 
 /*+F*************************************************************************
@@ -5955,11 +7074,14 @@
   unsigned char brdctl;
 
   aic_outb(p, BRDRW | BRDCS, BRDCTL);
+  udelay(1);
   aic_outb(p, 0, BRDCTL);
+  udelay(1);
   brdctl = aic_inb(p, BRDCTL);
+  udelay(1);
   *int_50 = !(brdctl & BRDDAT5);
   *ext_present = !(brdctl & BRDDAT6);
-  *eeprom = ( aic_inb(p, SPIOCAP) & EEPROM ) != 0;
+  *eeprom = (aic_inb(p, SPIOCAP) & EEPROM);
 }
 
 /*+F*************************************************************************
@@ -6002,12 +7124,37 @@
   brdctl = read_brdctl(p);
 
   *ext_present = !(brdctl & BRDDAT6);
-  *eeprom = (brdctl & BRDDAT7);
+  *eeprom = !(brdctl & BRDDAT7);
 
   /*
    * We're done, the calling function will release the SEEPROM for us
    */
-  
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic787x_ultra2_term_detect
+ *
+ * Description:
+ *   Detect the termination settings present on ultra2 class controllers
+ *
+ * NOTE: This functions assumes the SEEPROM will have already been aquired
+ *       prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low,
+                           int *enableSE_high, int *enableLVD_low,
+                           int *enableLVD_high, int *eprom_present)
+{
+  unsigned char brdctl;
+
+  brdctl = read_brdctl(p);
+
+  *eprom_present  = (brdctl & BRDDAT7);
+  *enableSE_high  = (brdctl & BRDDAT6);
+  *enableSE_low   = (brdctl & BRDDAT5);
+  *enableLVD_high = (brdctl & BRDDAT4);
+  *enableLVD_low  = (brdctl & BRDDAT3);
 }
 
 /*+F*************************************************************************
@@ -6019,146 +7166,192 @@
  *   SEEPROMs available.
  *-F*************************************************************************/
 static void
-configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
-    unsigned short adapter_control, unsigned char max_targ)
+configure_termination(struct aic7xxx_host *p)
 {
-  int internal50_present;
+  int internal50_present = 0;
   int internal68_present = 0;
   int external_present = 0;
-  int eprom_present;
-  int high_on;
-  int low_on;
+  int eprom_present = 0;
+  int enableSE_low = 0;
+  int enableSE_high = 0;
+  int enableLVD_low = 0;
+  int enableLVD_high = 0;
+  unsigned char brddat = 0;
+  unsigned char max_target = 0;
+  unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1);
 
   if (acquire_seeprom(p))
   {
-    if (adapter_control & CFAUTOTERM)
-    {
-      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination on "
-                       "controller:\n");
-      printk(KERN_INFO "aic7xxx: <%s> at ", board_names[p->board_name_index]);
-      switch(p->type & 0x1ff1)
-      {
-        case AHC_AIC7770:
-        case AHC_274:
-          printk("EISA slot %d\n", p->pci_device_fn);
-          break;
-        case AHC_284:
-          printk("VLB slot %d\n", p->pci_device_fn);
-          break;
-        default:
-          printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn),
-            PCI_FUNC(p->pci_device_fn));
-          break;
+    if (p->features & (AHC_WIDE|AHC_TWIN))
+      max_target = 16;
+    else
+      max_target = 8;
+    aic_outb(p, SEEMS | SEECS, SEECTL);
+    sxfrctl1 &= ~STPWEN;
+    if ( (p->adapter_control & CFAUTOTERM) ||
+         (p->features & AHC_ULTRA2) )
+    {
+      if ( (p->adapter_control & CFAUTOTERM) && !(p->features & AHC_ULTRA2) )
+      {
+        printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n",
+               p->host_no);
+        printk(KERN_INFO "(scsi%d) Please verify driver detected settings are "
+          "correct.\n", p->host_no);
+        printk(KERN_INFO "(scsi%d) If not, then please properly set the device "
+          "termination\n", p->host_no);
+        printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting CTRL-A "
+          "when prompted\n", p->host_no);
+        printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no);
       }
-      printk(KERN_INFO "aic7xxx: Please verify driver detected settings are "
-        "correct.\n");
-      printk(KERN_INFO "aic7xxx: If not, then please properly set the device "
-        "termination\n");
-      printk(KERN_INFO "aic7xxx: in the Adaptec SCSI BIOS by hitting CTRL-A "
-        "when prompted\n");
-      printk(KERN_INFO "aic7xxx: during machine bootup.\n");
       /* Configure auto termination. */
-      aic_outb(p, SEECS | SEEMS, SEECTL);
 
-      if ( (p->type & AHC_AIC7860) == AHC_AIC7860 )
+      if (p->features & AHC_ULTRA2)
       {
-        aic785x_cable_detect(p, &internal50_present, &external_present,
-          &eprom_present);
+        if (aic7xxx_override_term == -1)
+          aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high,
+                                     &enableLVD_low, &enableLVD_high,
+                                     &eprom_present);
+        if (!(p->adapter_control & CFSEAUTOTERM))
+        {
+          enableSE_low = (p->adapter_control & CFSTERM);
+          enableSE_high = (p->adapter_control & CFWSTERM);
+        }
+        if (!(p->adapter_control & CFAUTOTERM))
+        {
+          enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM);
+        }
+        internal50_present = 0;
+        internal68_present = 1;
+        external_present = 1;
       }
-      else
+      else if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 )
       {
         aic787x_cable_detect(p, &internal50_present, &internal68_present,
           &external_present, &eprom_present);
       }
-      if (max_targ > 8)
-      {
-        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
       {
-        printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
-               internal50_present ? "YES" : "NO",
-               external_present ? "YES" : "NO");
+        aic785x_cable_detect(p, &internal50_present, &external_present,
+          &eprom_present);
+      }
+      
+      if (max_target <= 8)
         internal68_present = 0;
+
+      if ( !(p->features & AHC_ULTRA2) )
+      {
+        if (max_target > 8)
+        {
+          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
+                 "Ext-68 %s)\n", p->host_no,
+                 internal50_present ? "YES" : "NO",
+                 internal68_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
+        }
+        else
+        {
+          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n",
+                 p->host_no,
+                 internal50_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
+        }
       }
       if (aic7xxx_verbose & VERBOSE_PROBE2)
-        printk(KERN_INFO "aic7xxx: EEPROM %s present.\n",
+        printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
              eprom_present ? "is" : "is not");
 
       /*
        * Now set the termination based on what we found.  BRDDAT6
        * controls wide termination enable.
+       * Flash Enable = BRDDAT7
+       * SE High Term Enable = BRDDAT6
+       * SE Low Term Enable = BRDDAT5 (7890)
+       * LVD High Term Enable = BRDDAT4 (7890)
        */
-      high_on = FALSE;
-      low_on = FALSE;
-      if ((max_targ > 8) &&
-          ((external_present == 0) || (internal68_present == 0)))
+      if ( !(p->features & AHC_ULTRA2) &&
+           (internal50_present && internal68_present && external_present) )
       {
-        high_on = TRUE;
+        printk(KERN_INFO "(scsi%d) Illegal cable configuration!!  Only two\n",
+               p->host_no);
+        printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be "
+               "in use at a time!\n", p->host_no);
+        /*
+         * Force termination (low and high byte) on.  This is safer than
+         * leaving it completely off, especially since this message comes
+         * most often from motherboard controllers that don't even have 3
+         * connectors, but instead are failing the cable detection.
+         */
+        internal50_present = external_present = 0;
+        enableSE_high = enableSE_low = 1;
       }
 
-      if ( ( (internal50_present ? 1 : 0) +
-             (internal68_present ? 1 : 0) +
-             (external_present   ? 1 : 0) ) <= 1)
+      if ((max_target > 8) &&
+          ((external_present == 0) || (internal68_present == 0) ||
+           (enableSE_high != 0)))
       {
-        low_on = TRUE;
+        brddat |= BRDDAT6;
+        p->flags |= AHC_TERM_ENB_SE_HIGH;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+                 p->host_no);
       }
-          
-      if (internal50_present && internal68_present && external_present)
+
+      if ( (((internal50_present ? 1 : 0) +
+             (internal68_present ? 1 : 0) +
+             (external_present   ? 1 : 0)) <= 1) ||
+           (enableSE_low != 0) )
       {
-        printk(KERN_INFO "aic7xxx: Illegal cable configuration!!  Only two\n");
-        printk(KERN_INFO "aic7xxx: connectors on the SCSI controller may be "
-               "in use at a time!\n");
+        if (p->features & AHC_ULTRA2)
+          brddat |= BRDDAT5;
+        else
+          sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_SE_LOW;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+                 p->host_no);
       }
 
-      if (high_on == TRUE)
-        write_brdctl(p, BRDDAT6);
-      else
-        write_brdctl(p, 0);
-
-      if (low_on == TRUE)
-        *sxfrctl1 |= STPWEN;
-
-      if (max_targ > 8)
+      if (enableLVD_low != 0)
       {
-        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
-              low_on ? "ON" : "OFF", high_on ? "ON" : "OFF");
+        sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_LVD;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) LVD Low byte termination Enabled\n",
+                 p->host_no);
       }
-      else
+          
+      if (enableLVD_high != 0)
       {
-        printk(KERN_INFO "aic7xxx: Termination %s\n",
-          low_on ? "Enabled" : "Disabled");
+        brddat |= BRDDAT4;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) LVD High byte termination Enabled\n",
+                 p->host_no);
       }
     }
     else
     {
-      if (adapter_control & CFSTERM)
-        *sxfrctl1 |= STPWEN;
-
-      aic_outb(p, SEEMS | SEECS, SEECTL);
-      /*
-       * Configure high byte termination.
-       */
-      if (adapter_control & CFWSTERM)
+      if (p->adapter_control & CFSTERM)
       {
-        write_brdctl(p, BRDDAT6);
-      }
-      else
-      {
-        write_brdctl(p, 0);
+        if (p->features & AHC_ULTRA2)
+          brddat |= BRDDAT5;
+        else
+          sxfrctl1 |= STPWEN;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+                 p->host_no);
       }
-      if (aic7xxx_verbose & VERBOSE_PROBE2)
+
+      if (p->adapter_control & CFWSTERM)
       {
-        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
-               (adapter_control & CFSTERM) ? "ON" : "OFF",
-               (adapter_control & CFWSTERM) ? "ON" : "OFF");
+        brddat |= BRDDAT6;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+                 p->host_no);
       }
     }
+    write_brdctl(p, brddat);
     release_seeprom(p);
+    aic_outb(p, sxfrctl1, SXFRCTL1);
   }
 }
 
@@ -6200,7 +7393,12 @@
       aic_outb(p, i, SCBPTR);
       aic_outb(p, 0, SCB_CONTROL);   /* Clear the control byte. */
       aic_outb(p, i + 1, SCB_NEXT);  /* Set the next pointer. */
+      aic_outb(p, i - 1, SCB_PREV);  /* Set the prev pointer. */
       aic_outb(p, SCB_LIST_NULL, SCB_TAG);  /* Make the tag invalid. */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS);  /* no busy untagged */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2);
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3);
     }
 
     /* Make sure the last SCB terminates the free list. */
@@ -6212,6 +7410,11 @@
     aic_outb(p, 0, SCB_CONTROL);
 
     p->scb_data->maxhscbs = i;
+    /*
+     * Use direct indexing instead for speed
+     */
+    if ( i == AIC7XXX_MAXSCB )
+      p->flags &= ~AHC_PAGESCBS;
   }
 
 }
@@ -6230,9 +7433,7 @@
   int i, result;
   int max_targets;
   int found = 1;
-  unsigned char target_settings;
-  unsigned char term, scsi_conf, sxfrctl1;
-  unsigned short ultraenable = 0;
+  unsigned char term, scsi_conf;
   struct Scsi_Host *host;
 
   /*
@@ -6253,11 +7454,11 @@
   host->n_io_port = 0xFF;
   host->base = (unsigned char *) p->mbase;
   host->irq = p->irq;
-  if (p->type & AHC_WIDE)
+  if (p->features & AHC_WIDE)
   {
     host->max_id = 16;
   }
-  if (p->type & AHC_TWIN)
+  if (p->features & AHC_TWIN)
   {
     host->max_channel = 1;
   }
@@ -6265,6 +7466,7 @@
   p->host = host;
   p->last_reset = 0;
   p->host_no = host->host_no;
+  host->unique_id = p->instance;
   p->isr_count = 0;
   p->next = NULL;
   p->completeq.head = NULL;
@@ -6287,7 +7489,7 @@
   for (i = 0; i < MAX_TARGETS; i++)
   {
     p->dev_commands_sent[i] = 0;
-    p->dev_flags[i] = DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+    p->dev_flags[i] = 0;
     p->dev_active_cmds[i] = 0;
     p->dev_last_reset[i] = 0;
     p->dev_last_queue_full[i] = 0;
@@ -6300,19 +7502,16 @@
     p->dev_timer[i].expires = 0;
     p->dev_timer[i].data = (unsigned long)p;
     p->dev_timer[i].function = (void *)aic7xxx_timer;
-    p->syncinfo[i].period = 0;
-    p->syncinfo[i].offset = 0;
   }
 
   printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
     board_names[p->board_name_index]);
-  switch(p->type & 0x1ff1)
+  switch(p->chip)
   {
-    case AHC_AIC7770:
-    case AHC_274:
+    case (AHC_AIC7770|AHC_EISA):
       printk("EISA slot %d\n", p->pci_device_fn);
       break;
-    case AHC_284:
+    case (AHC_AIC7770|AHC_VL):
       printk("VLB slot %d\n", p->pci_device_fn);
       break;
     default:
@@ -6320,7 +7519,7 @@
         PCI_FUNC(p->pci_device_fn));
       break;
   }
-  if (p->type & AHC_TWIN)
+  if (p->features & AHC_TWIN)
   {
     printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
            p->host_no, p->scsi_id, p->scsi_id_b);
@@ -6331,7 +7530,7 @@
 
     channel = "";
 
-    if ((p->type & AHC_39x) != 0)
+    if ((p->flags & AHC_MULTI_CHANNEL) != 0)
     {
       channel = " A";
 
@@ -6340,7 +7539,7 @@
         channel = (p->flags & AHC_CHNLB) ? " B" : " C";
       }
     }
-    if (p->type & AHC_WIDE)
+    if (p->features & AHC_WIDE)
     {
       printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
     }
@@ -6357,257 +7556,190 @@
    */
   detect_maxscb(p);
   printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
-  printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
-    p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
-    p->base, p->irq);
-  printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
-    p->host_no, p->mbase, (unsigned long)p->maddr);
-
-
+  if (aic7xxx_verbose & VERBOSE_PROBE2)
+  {
+    printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
+      p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
+      p->base, p->irq);
+    printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
+      p->host_no, p->mbase, (unsigned long)p->maddr);
+  }
 
+#ifdef CONFIG_PCI
   /*
-   * Register IRQ with the kernel.  Only allow sharing IRQs with
-   * PCI devices.
+   * Now that we know our instance number, we can set the flags we need to
+   * force termination if need be.
    */
-  if ((p->type & AHC_AIC7770) == AHC_AIC7770)
+  if (aic7xxx_stpwlev != -1)
   {
-    result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+    /*
+     * This option only applies to PCI controllers.
+     */
+    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
+    {
+      unsigned char devconfig;
+
+#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92)
+      pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig);
+#else
+      pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+                               DEVCONFIG, &devconfig);
+#endif
+      if ( (aic7xxx_stpwlev >> p->instance) & 0x01 )
+      {
+        devconfig |= 0x02;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("(scsi%d) Force setting STPWLEV bit\n", p->host_no);
+      }
+      else
+      {
+        devconfig &= ~0x02;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("(scsi%d) Force clearing STPWLEV bit\n", p->host_no);
+      }
+#if LINUX_KERNEL_VERSION > KERNEL_VERSION(2,1,92)
+      pci_write_config_byte(p->pdev, DEVCONFIG, devconfig);
+#else
+      pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+                                DEVCONFIG, devconfig);
+#endif
+    }
   }
-  else
+#endif
+
+  /*
+   * That took care of devconfig and stpwlev, now for the actual termination
+   * settings.
+   */
+  if (aic7xxx_override_term != -1)
   {
-    result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
-              "aic7xxx", p));
-    if (result < 0)
+    /*
+     * Again, this only applies to PCI controllers.  We don't have problems
+     * with the termination on 274x controllers to the best of my knowledge.
+     */
+    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
     {
-      result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
-              "aic7xxx", p));
+      unsigned char term_override;
+
+      term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f);
+      p->adapter_control &= 
+        ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM);
+      if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) )
+      {
+        p->adapter_control |= CFLVDSTERM;
+      }
+      if (term_override & 0x02)
+      {
+        p->adapter_control |= CFWSTERM;
+      }
+      if (term_override & 0x01)
+      {
+        p->adapter_control |= CFSTERM;
+      }
     }
   }
-  if (result < 0)
+
+  if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) )
   {
-    printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring.\n",
-           p->host_no, p->irq);
-    return (0);
+    if (p->features & AHC_SPIOCAP)
+    {
+      if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
+      /*
+       * Update the settings in sxfrctl1 to match the termination
+       * settings.
+       */
+        configure_termination(p);
+    }
+    else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
+    {
+      configure_termination(p);
+    }
   }
 
   /*
+   * Clear out any possible pending interrupts.
+   */
+  aic7xxx_clear_intstat(p);
+
+  /*
    * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
    */
-  if (p->type & AHC_TWIN)
+  if (p->features & AHC_TWIN)
   {
-    /*
-     * The controller is gated to channel B after a chip reset; set
-     * bus B values first.
-     */
+    /* Select channel B */
+    aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+
     term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0;
     aic_outb(p, p->scsi_id_b, SCSIID);
     scsi_conf = aic_inb(p, SCSICONF + 1);
-    sxfrctl1 = aic_inb(p, SXFRCTL1);
-    aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | 
+    aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+    aic_outb(p, (scsi_conf & ENSPCHK) | term | 
          ENSTIMER | ACTNEGEN, SXFRCTL1);
+    aic_outb(p, 0, SIMODE0);
     aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
-    if (p->type & AHC_ULTRA)
-    {
-      aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
-    }
-    else
-    {
-      aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-    }
-    if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
-    {
-      scsi_conf &= ~0x07;
-      scsi_conf |= p->scsi_id_b;
-      aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF + 1);
-    }
-    if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
-    {
-      /* Reset SCSI bus B. */
-      if (aic7xxx_verbose & VERBOSE_PROBE)
-        printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
-
-      aic7xxx_reset_current_bus(p);
-    }
+    aic_outb(p, 0, SCSIRATE);
 
     /* Select channel A */
-    aic_outb(p, SELNARROW, SBLKCTL);
+    aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
   }
 
-  term = ((p->flags & AHC_TERM_ENB_A) != 0) ? STPWEN : 0;
-  aic_outb(p, p->scsi_id, SCSIID);
+  term = ((p->flags & AHC_TERM_ENB_SE_LOW) != 0) ? STPWEN : 0;
+  if (p->features & AHC_ULTRA2)
+    aic_outb(p, p->scsi_id, SCSIID_ULTRA2);
+  else
+    aic_outb(p, p->scsi_id, SCSIID);
   scsi_conf = aic_inb(p, SCSICONF);
-  sxfrctl1 = aic_inb(p, SXFRCTL1);
-  aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | 
+  aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+  aic_outb(p, (scsi_conf & ENSPCHK) | term | 
        ENSTIMER | ACTNEGEN, SXFRCTL1);
+  aic_outb(p, 0, SIMODE0);
   aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
-  if (p->type & AHC_ULTRA)
+  aic_outb(p, 0, SCSIRATE);
+  if ( p->features & AHC_ULTRA2)
+    aic_outb(p, 0, SCSIOFFSET);
+
+  /*
+   * Look at the information that board initialization or the board
+   * BIOS has left us. In the lower four bits of each target's
+   * scratch space any value other than 0 indicates that we should
+   * initiate synchronous transfers. If it's zero, the user or the
+   * BIOS has decided to disable synchronous negotiation to that
+   * target so we don't activate the needsdtr flag.
+   */
+  if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0)
   {
-    aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
+    max_targets = 8;
   }
   else
   {
-    aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+    max_targets = 16;
   }
-  if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+
+  if (!(aic7xxx_no_reset))
   {
-    scsi_conf &= ~0x07;
-    scsi_conf |= p->scsi_id;
-    aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF);
-  }
-
-
-  if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
-  {
-    /* Reset SCSI bus A. */
-    if (aic7xxx_verbose & VERBOSE_PROBE)
-    {  /* In case we are a 3940, 3985, or 7895, print the right channel */
-      char *channel = "";
-      if (p->flags & AHC_MULTI_CHANNEL)
-      {
-        channel = " A";
-        if (p->flags & (AHC_CHNLB|AHC_CHNLC))
-          channel = (p->flags & AHC_CHNLB) ? " B" : " C";
-      }
-      printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
-    }
-
-    aic7xxx_reset_current_bus(p);
-
     /*
-     * Delay for the reset delay.
+     * If we reset the bus, then clear the transfer settings, else leave
+     * them be
      */
-    if (!reset_delay)
-      aic7xxx_delay(AIC7XXX_RESET_DELAY);
-  }
-
-  /*
-   * Look at the information that board initialization or the board
-   * BIOS has left us. In the lower four bits of each target's
-   * scratch space any value other than 0 indicates that we should
-   * initiate synchronous transfers. If it's zero, the user or the
-   * BIOS has decided to disable synchronous negotiation to that
-   * target so we don't activate the needsdtr flag.
-   */
-  p->needsdtr_copy = 0x0;
-  p->sdtr_pending = 0x0;
-  p->needwdtr_copy = 0x0;
-  p->wdtr_pending = 0x0;
-  p->tagenable = 0x0;
-  p->ultraenb = 0x0;
-  p->discenable = 0xffff;
-  if ((p->type & (AHC_TWIN|AHC_WIDE)) == 0)
-  {
-    max_targets = 8;
-  }
-  else
-  {
-    max_targets = 16;
-  }
-
-  /*
-   * Grab the disconnection disable table and invert it for our needs
-   */
-  if (p->flags & AHC_USEDEFAULTS)
-  {
-    printk(KERN_INFO "(scsi%d) Host adapter BIOS disabled. Using default SCSI "
-           "device parameters.\n", p->host_no);
-    if (p->type & AHC_ULTRA)
-      p->ultraenb = 0xffff;
-    p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
-  }
-  else
-  {
-    p->discenable = ~((aic_inb(p, DISC_DSB + 1) << 8) |
-        aic_inb(p, DISC_DSB));
-    if (p->type & AHC_ULTRA)
-      p->ultraenb = (aic_inb(p, ULTRA_ENB + 1) << 8) | 
-          aic_inb(p, ULTRA_ENB);
-  }
-
-  for (i = 0; i < max_targets; i++)
-  {
-    if (p->flags & AHC_USEDEFAULTS)
-    {
-      target_settings = 0;  /* 10 or 20 MHz depending on Ultra enable */
-      p->needsdtr_copy |= (0x01 << i);
-      p->needwdtr_copy |= (0x01 << i);
-      if (p->type & AHC_ULTRA)
-        ultraenable |= (0x01 << i);
-    }
-    else
+    for (i = 0; i < max_targets; i++)
     {
-      target_settings = aic_inb(p, TARG_SCRATCH + i);
-      if (target_settings & 0x0F)
-      {
-        p->needsdtr_copy |= (0x01 << i);
-        /*
-         * Default to asynchronous transfers (0 offset)
-         */
-        target_settings &= 0xF0;
-      }
-      if (target_settings & 0x80)
+      aic_outb(p, 0, TARG_SCSIRATE + i);
+      if (p->features & AHC_ULTRA2)
       {
-        p->needwdtr_copy |= (0x01 << i);
-        /*
-         * Clear the wide flag. When wide negotiation is successful,
-         * we'll enable it.
-         */
-        target_settings &= 0x7F;
+        aic_outb(p, 0, TARG_OFFSET + i);
       }
+      p->transinfo[i].cur_offset = 0;
+      p->transinfo[i].cur_period = 0;
+      p->transinfo[i].cur_width = MSG_EXT_WDTR_BUS_8_BIT;
     }
 
     /*
-     * If we reset the bus, then clear the transfer ssettings, else leave
-     * them be
+     * If we reset the bus, then clear the transfer settings, else leave
+     * them be.
      */
-    if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
-      aic_outb(p, target_settings, TARG_SCRATCH + i);
-    if (p->needsdtr_copy & (0x01 << i))
-    {
-      short sxfr, j;
-
-      sxfr = target_settings & SXFR;
-      if ((p->ultraenb & (1 << i)) != 0)
-      {
-        /* Want an ultra speed in the table */
-        sxfr |= 0x100;
-      }
-      for (j = 0; j < NUMBER(aic7xxx_syncrates); j++)
-      {
-        if (sxfr == aic7xxx_syncrates[j].rate)
-          break;
-      }
-      p->syncinfo[i].period = aic7xxx_syncrates[j].period;
-      p->syncinfo[i].offset =
-       (p->type & AHC_WIDE) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT;
-    }
-    else
-    {
-      p->syncinfo[i].period = 0;
-      p->syncinfo[i].offset = 0;
-    }
-  }
-
-  /*
-   * 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->type & AHC_WIDE))
-  {
-    p->needwdtr_copy = 0;
-  }
-  p->needsdtr = p->needsdtr_copy;
-  p->needwdtr = p->needwdtr_copy;
-
-  /*
-   * If we reset the bus, then clear the transfer ssettings, else leave
-   * them be
-   */
-  if ( (scsi_conf & RESET_SCSI) && !(aic7xxx_no_reset) )
-  {
     aic_outb(p, 0, ULTRA_ENB);
     aic_outb(p, 0, ULTRA_ENB + 1);
+    p->ultraenb = 0;
   }
 
   /*
@@ -6617,21 +7749,35 @@
    * 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;
+    unsigned long temp;
 
     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)
+    {
+      /*
+       * A little padding so we can align thing the way we want
+       */
+      p->scb_data->hscbs = kmalloc(array_size + 0x1f, GFP_ATOMIC);
+    }
     if (p->scb_data->hscbs == NULL)
     {
       printk("(scsi%d) Unable to allocate hardware SCB array; "
              "failing detection.\n", p->host_no);
-      release_region(p->base, MAXREG - MINREG);
-      free_irq(p->irq, p);
+      p->irq = 0;
       return(0);
     }
+    /*
+     * Save the actual kmalloc buffer pointer off, then align our
+     * buffer to a 32 byte boundary
+     */
+    p->scb_data->hscb_kmalloc_ptr = p->scb_data->hscbs;
+    temp = (unsigned long)p->scb_data->hscbs;
+    temp += 0x1f;
+    temp &= ~0x1f;
+    p->scb_data->hscbs = (struct aic7xxx_hwscb *)temp;
     /* At least the control byte of each SCB needs to be 0. */
     memset(p->scb_data->hscbs, 0, array_size);
 
@@ -6648,12 +7794,19 @@
     aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
     aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
     aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
+  }
 
-    /* The Q-FIFOs we just set up are all empty */
-    aic_outb(p, 0, QINPOS);
-    aic_outb(p, 0, KERNEL_QINPOS);
-    aic_outb(p, 0, QOUTPOS);
-
+  /* The Q-FIFOs we just set up are all empty */
+  aic_outb(p, 0, QINPOS);
+  aic_outb(p, 0, KERNEL_QINPOS);
+  aic_outb(p, 0, QOUTPOS);
+
+  if(p->features & AHC_QUEUE_REGS)
+  {
+    aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA);
+    aic_outb(p, 0, SDSCB_QOFF);
+    aic_outb(p, 0, SNSCB_QOFF);
+    aic_outb(p, 0, HNSCB_QOFF);
   }
 
   /*
@@ -6666,32 +7819,137 @@
    * Message out buffer starts empty
    */
   aic_outb(p, MSG_NOOP, MSG_OUT);
+  aic_outb(p, MSG_NOOP, LAST_MSG);
+
+  /*
+   * Set all the other asundry items that haven't been set yet.
+   * This includes just dumping init values to a lot of registers simply
+   * to make sure they've been touched and are ready for use parity wise
+   * speaking.
+   */
+  aic_outb(p, 0, TMODE_CMDADDR);
+  aic_outb(p, 0, TMODE_CMDADDR + 1);
+  aic_outb(p, 0, TMODE_CMDADDR + 2);
+  aic_outb(p, 0, TMODE_CMDADDR + 3);
+  aic_outb(p, 0, TMODE_CMDADDR_NEXT);
+
+  /*
+   * Link us into the list of valid hosts
+   */
+  p->next = first_aic7xxx;
+  first_aic7xxx = p;
+
+  /*
+   * Clear out any possible pending interrupts, again.
+   */
+  aic7xxx_clear_intstat(p);
+
+  /*
+   * Allocate the first set of scbs for this controller.  This is to stream-
+   * line code elsewhere in the driver.  If we have to check for the existence
+   * of scbs in certain code sections, it slows things down.  However, as
+   * soon as we register the IRQ for this card, we could get an interrupt that
+   * includes possibly the SCSI_RSTI interrupt.  If we catch that interrupt
+   * then we are likely to segfault if we don't have at least one chunk of
+   * SCBs allocated or add checks all through the reset code to make sure
+   * that the SCBs have been allocated which is an invalid running condition
+   * and therefore I think it's preferable to simply pre-allocate the first
+   * chunk of SCBs.
+   */
+  aic7xxx_allocate_scb(p);
 
   /*
    * Load the sequencer program, then re-enable the board -
    * resetting the AIC-7770 disables it, leaving the lights
-   * on with nobody home. On the PCI bus you *may* be home,
-   * but then your mailing address is dynamically assigned
-   * so no one can find you anyway :-)
+   * on with nobody home.
    */
   aic7xxx_loadseq(p);
 
-  if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
   {
     aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
   }
 
-  /*
-   * Link us into the list of valid hosts
-   */
-  p->next = first_aic7xxx;
-  first_aic7xxx = p;
+  if ( !(aic7xxx_no_reset) )
+  {
+    if (p->features & AHC_TWIN)
+    {
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
+      aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+      aic7xxx_reset_current_bus(p);
+      aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+    }
+    /* Reset SCSI bus A. */
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {  /* In case we are a 3940, 3985, or 7895, print the right channel */
+      char *channel = "";
+      if (p->flags & AHC_MULTI_CHANNEL)
+      {
+        channel = " A";
+        if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+          channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+      }
+      printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
+    }
+    
+    /*
+     * Some of the new Ultra2 chipsets need a longer delay after a chip
+     * reset than just the init setup creates, so we have to delay here
+     * before we go into a reset in order to make the chips happy.
+     */
+    if (p->features & AHC_ULTRA2)
+      mdelay(250);
+    aic7xxx_reset_current_bus(p);
 
+    /*
+     * Delay for the reset delay.
+     */
+    if (!reset_delay)
+      aic7xxx_delay(AIC7XXX_RESET_DELAY);
+  }
+  else
+  {
+    if (!reset_delay)
+    {
+      printk(KERN_INFO "(scsi%d) Not resetting SCSI bus.  Note: Don't use "
+             "the no_reset\n", p->host_no);
+      printk(KERN_INFO "(scsi%d) option unless you have a verifiable need "
+             "for it.\n", p->host_no);
+      printk(KERN_INFO "(scsi%d) The no_reset option is known to break some "
+             "systems,\n", p->host_no);
+      printk(KERN_INFO "(scsi%d) and is not supported by the driver author\n",
+             p->host_no);
+      aic7xxx_delay(AIC7XXX_RESET_DELAY);
+    }
+  }
+  
   /*
-   * 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.
+   * Register IRQ with the kernel.  Only allow sharing IRQs with
+   * PCI devices.
    */
+  if (!(p->chip & AHC_PCI))
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+  }
+  else
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
+              "aic7xxx", p));
+    if (result < 0)
+    {
+      result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+              "aic7xxx", p));
+    }
+  }
+  if (result < 0)
+  {
+    printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring "
+           "controller.\n", p->host_no, p->irq);
+    p->irq = 0;
+    return (0);
+  }
+
   unpause_sequencer(p, /* unpause_always */ TRUE);
 
   return (found);
@@ -6708,12 +7966,9 @@
 int
 aic7xxx_chip_reset(struct aic7xxx_host *p)
 {
-  unsigned char hcntrl;
+  unsigned char sblkctl;
   int wait;
 
-  /* Retain the IRQ type across the chip reset. */
-  hcntrl = (aic_inb(p, 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.
@@ -6724,29 +7979,26 @@
    * 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) && ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0))
+  wait = 1000;  /* 1 second (1000 * 1 msec) */
+  while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK))
   {
-    udelay(1000);  /* 1 msec = 1000 usec */
-    wait = wait - 1;
+    mdelay(1);  /* 1 msec */
   }
 
-  if ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0)
-  {
-    printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
-  }
-
-  aic_outb(p, hcntrl | PAUSE, HCNTRL);
+  pause_sequencer(p);
 
-  switch( aic_inb(p, SBLKCTL) & 0x0a )
+  sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE);
+  if (p->chip & AHC_PCI)
+    sblkctl &= ~SELBUSB;
+  switch( sblkctl )
   {
     case 0:  /* normal narrow card */
       break;
     case 2:  /* Wide card */
-      p->type |= AHC_WIDE;
+      p->features |= AHC_WIDE;
       break;
     case 8:  /* Twin card */
-      p->type |= AHC_TWIN;
+      p->features |= AHC_TWIN;
       p->flags |= AHC_MULTI_CHANNEL;
       break;
     default: /* hmmm...we don't know what this is */
@@ -6770,6 +8022,7 @@
 {
   struct aic7xxx_host *p = NULL;
   struct Scsi_Host *host;
+  int i;
 
   /*
    * Allocate a storage area by registering us with the mid-level
@@ -6781,6 +8034,7 @@
   {
     p = (struct aic7xxx_host *) host->hostdata;
     memset(p, 0, sizeof(struct aic7xxx_host));
+    *p = *temp;
     p->host = host;
 
     p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
@@ -6795,34 +8049,20 @@
        * For some reason we don't have enough memory.  Free the
        * allocated memory for the aic7xxx_host struct, and return NULL.
        */
+      release_region(p->base, MAXREG - MINREG);
       scsi_unregister(host);
-      p = NULL;
+      return(NULL);
     }
-    if (p != NULL)
-    {
-      p->host_no = host->host_no;
-      p->base = temp->base;
-      p->mbase = temp->mbase;
-      p->maddr = temp->maddr;
-      p->flags = temp->flags;
-      p->type = temp->type;
-      p->unpause = temp->unpause;
-      p->pause = temp->pause;
-      p->pci_bus = temp->pci_bus;
-      p->pci_device_fn = temp->pci_device_fn;
-      p->bios_address = temp->bios_address;
-      p->irq = temp->irq;
-      p->scsi_id = temp->scsi_id;
-      p->scsi_id_b = temp->scsi_id_b;
-      p->discenable = temp->discenable;
-      p->ultraenb = temp->ultraenb;
-      p->tagenable = 0;
-      p->orderedtag = 0;
-      p->board_name_index = temp->board_name_index;
-      p->adapter_control = temp->adapter_control;
-      p->bios_control = temp->bios_control;
-      DRIVER_LOCK_INIT
+    p->host_no = host->host_no;
+    p->tagenable = 0;
+    p->orderedtag = 0;
+    for (i=0; i<MAX_TARGETS; i++)
+    {
+      p->transinfo[i].goal_period = 0;
+      p->transinfo[i].goal_offset = 0;
+      p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
     }
+    DRIVER_LOCK_INIT
   }
   return (p);
 }
@@ -6838,28 +8078,35 @@
 static void
 aic7xxx_free(struct aic7xxx_host *p)
 {
-  int i, jump;
+  int i;
 
   /*
    * Free the allocated hardware SCB space.
    */
-  if (p->scb_data->hscbs != NULL)
+  if (p->scb_data != NULL)
   {
-    kfree(p->scb_data->hscbs);
-  }
-  /*
-   * Free the driver SCBs.  These were allocated on an as-need
-   * basis. However, we allocated them 30 at a time up until the
-   * very last allocation (if there was one).  So, we need to free
-   * every 30th pointer to free the array (this also frees the
-   * SG_array structs as well).
-   *
-   * Note, on 64 bit machines we allocate 29 at a time instead.
-   */
-  jump = (sizeof(int) == sizeof(void *)) ? 30 : 29;
-  for (i = 0; i < p->scb_data->numscbs; i += jump)
-  {
-    kfree(p->scb_data->scb_array[i]);
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscb_kmalloc_ptr);
+      p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL;
+    }
+    /*
+     * Free the driver SCBs.  These were allocated on an as-need
+     * basis.  We allocated these in groups depending on how many
+     * we could fit into a given amount of RAM.  The tail SCB for
+     * these allocations has a pointer to the alloced area.
+     */
+    for (i = 0; i < p->scb_data->numscbs; i++)
+    {
+      if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL)
+        kfree(p->scb_data->scb_array[i]->kmalloc_ptr);
+      p->scb_data->scb_array[i] = NULL;
+    }
+  
+    /*
+     * Free the SCB data area.
+     */
+    kfree(p->scb_data);
   }
 
   /*
@@ -6874,10 +8121,6 @@
       kfree(p->dev_sdtr_cmnd[i]);
   }
 
-  /*
-   * Free the SCB data area.
-   */
-  kfree(p->scb_data);
 }
 
 /*+F*************************************************************************
@@ -6888,12 +8131,12 @@
  *   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)
+static void
+aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
 {
   int have_seeprom = 0;
-  int i, max_targets;
-  unsigned char target_settings, scsi_conf;
+  int i, max_targets, mask;
+  unsigned char scsirate, scsi_conf;
   unsigned short scarray[128];
   struct seeprom_config *sc = (struct seeprom_config *) scarray;
 
@@ -6901,42 +8144,31 @@
   {
     printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
   }
-  switch (p->type & 0x00001ff1)
+  switch (p->chip)
   {
-    case AHC_AIC7770:  /* None of these adapters have seeproms. */
-    case AHC_274:
+    case (AHC_AIC7770|AHC_EISA):  /* None of these adapters have seeproms. */
+      if (aic_inb(p, SCSICONF) & TERM_ENB)
+        p->flags |= AHC_TERM_ENB_A;
+      if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
+        p->flags |= AHC_TERM_ENB_B;
       break;
 
-    case AHC_284:
+    case (AHC_AIC7770|AHC_VL):
       have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
       break;
 
-    case AHC_AIC7850:  /* The 2910B is a 7850 with a seeprom. */
-    case AHC_294AU:
-    case AHC_AIC7870:  /* For these controllers we try the three possible */
-    case AHC_AIC7895:  /* SEEPROM read types.  If none works, then we are */
-    case AHC_294:      /* SOL.  This should catch any SEEPROM variety     */
-    case AHC_394:      /* Adaptec or some motherboard manufacturer might  */
-    case AHC_294U:     /* throw at us, and since we perform a checksum    */
-    case AHC_394U:     /* during the read, we should get bogus seeprom    */
-    case AHC_AIC7860:  /* reads. */
-    case AHC_AIC7880:
-    case AHC_398:
-    case AHC_398U:
+    default:
       have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(*sc)/2, C46);
-      if (!have_seeprom)
-        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(scarray)/2, C46);
-      if (!have_seeprom)
-        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(*sc)/2, C56_66);
+                                  scarray, p->sc_size, p->sc_type);
       if (!have_seeprom)
-        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, sizeof(scarray)/2, C56_66);
-      break;
-
-    default:
+      {
+        if(p->sc_type == C46)
+          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                      scarray, p->sc_size, C56_66);
+        else
+          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                      scarray, p->sc_size, C46);
+      }
       break;
   }
 
@@ -6944,10 +8176,31 @@
   {
     if (aic7xxx_verbose & VERBOSE_PROBE2)
     {
-      printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+      printk("\naic7xxx: No SEEPROM available.\n");
+    }
+    p->flags |= AHC_NEWEEPROM_FMT;
+    if (aic_inb(p, SCSISEQ) == 0)
+    {
+      p->flags |= AHC_USEDEFAULTS;
+      p->flags &= ~AHC_BIOS_ENABLED;
+      p->scsi_id = p->scsi_id_b = 7;
+      *sxfrctl1 |= STPWEN;
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+      {
+        printk("aic7xxx: Using default values.\n");
+      }
+    }
+    else if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {
+      printk("aic7xxx: Using leftover BIOS values.\n");
+    }
+    if ( *sxfrctl1 & STPWEN )
+    {
+      p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+      sc->adapter_control &= ~CFAUTOTERM;
+      sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
     }
-    p->flags |= AHC_USEDEFAULTS;
-    p->flags &= ~AHC_BIOS_ENABLED;
+    p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
   }
   else
   {
@@ -6957,15 +8210,25 @@
     }
 
     /*
+     * Note things in our flags
+     */
+    p->flags |= AHC_SEEPROM_FOUND;
+
+    /*
      * Update the settings in sxfrctl1 to match the termination settings.
      */
     *sxfrctl1 = 0;
 
     /*
+     * Get our SCSI ID from the SEEPROM setting...
+     */
+    p->scsi_id = (sc->brtime_id & CFSCSIID);
+
+    /*
      * First process the settings that are different between the VLB
      * and PCI adapter seeproms.
      */
-    if (p->type & AHC_284)
+    if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770)
     {
       /* VLB adapter seeproms */
       if (sc->bios_control & CF284XEXTEND)
@@ -6974,157 +8237,256 @@
       if (sc->adapter_control & CF284XSTERM)
       {
         *sxfrctl1 |= STPWEN;
-        p->flags |= AHC_TERM_ENB_A;
+        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       }
-      /*
-       * 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 |= AHC_EXTEND_TRANS_A;
+      if (sc->bios_control & CFBIOSEN)
+        p->flags |= AHC_BIOS_ENABLED;
+      else
+        p->flags &= ~AHC_BIOS_ENABLED;
 
       if (sc->adapter_control & CFSTERM)
       {
         *sxfrctl1 |= STPWEN;
-        p->flags |= AHC_TERM_ENB_A;
+        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       }
-
-      /* Limit to 16 targets just in case. */
-      max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
     }
+    p->sc = *sc;
+  }
 
-    p->discenable = 0;
+  p->discenable = 0;
+    
+  /*
+   * Limit to 16 targets just in case.  The 2842 for one is known to
+   * blow the max_targets setting, future cards might also.
+   */
+  max_targets = MIN(sc->max_targets & CFMAXTARG,
+                   ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8));
 
+  if (have_seeprom)
+  {
     for (i = 0; i < max_targets; i++)
     {
-      if( (p->type & AHC_ULTRA) &&
-         !(sc->adapter_control & CFULTRAEN) &&
-          (sc->device_flags[i] & CFSYNCHISULTRA) )
+      if( ((p->features & AHC_ULTRA) &&
+          !(sc->adapter_control & CFULTRAEN) &&
+           (sc->device_flags[i] & CFSYNCHISULTRA)) ||
+          (sc->device_flags[i] & CFNEWULTRAFORMAT) )
       {
         p->flags |= AHC_NEWEEPROM_FMT;
         break;
       }
     }
+  }
 
-    for (i = 0; i < max_targets; i++)
+  for (i = 0; i < max_targets; i++)
+  {
+    mask = (0x01 << i);
+    if (!have_seeprom)
     {
-      target_settings = (sc->device_flags[i] & CFXFER) << 4;
-      if (sc->device_flags[i] & CFSYNCH)
+      if(aic_inb(p, SCSISEQ) != 0)
       {
-        target_settings |= SOFS;
+        /*
+         * OK...the BIOS set things up and left behind the settings we need.
+         * Just make our sc->device_flags[i] entry match what the card has
+         * set for this device.
+         */
+        p->discenable = 
+          ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) );
+        p->ultraenb =
+          (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) );
+        sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0;
+        if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER)
+          sc->device_flags[i] |= CFWIDEB;
+        if (p->features & AHC_ULTRA2)
+        {
+          if (aic_inb(p, TARG_OFFSET + i))
+          {
+            sc->device_flags[i] |= CFSYNCH;
+            sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07);
+            if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 )
+              sc->device_flags[i] |= CFSYNCHISULTRA;
+          }
+        }
+        else
+        {
+          if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER)
+          {
+            sc->device_flags[i] |= CFSYNCH;
+            if (p->features & AHC_ULTRA)
+              sc->device_flags[i] |= ((p->ultraenb & mask) ?
+                                      CFSYNCHISULTRA : 0);
+          }
+        }
       }
-      if (sc->device_flags[i] & CFWIDEB)
+      else
       {
-        target_settings |= WIDEXFER;
+        /*
+         * Assume the BIOS has NOT been run on this card and nothing between
+         * the card and the devices is configured yet.
+         */
+        sc->device_flags[i] = CFDISC;
+        if (p->features & AHC_WIDE)
+          sc->device_flags[i] |= CFWIDEB;
+        if (p->features & AHC_ULTRA2)
+          sc->device_flags[i] |= 3;
+        else if (p->features & AHC_ULTRA)
+          sc->device_flags[i] |= CFSYNCHISULTRA;
+        sc->device_flags[i] |= CFSYNCH;
+        aic_outb(p, 0, TARG_SCSIRATE + i);
+        if (p->features & AHC_ULTRA2)
+          aic_outb(p, 0, TARG_OFFSET + i);
       }
-      if (sc->device_flags[i] & CFDISC)
+    }
+    if (sc->device_flags[i] & CFDISC)
+    {
+      p->discenable |= mask;
+    }
+    if (p->flags & AHC_NEWEEPROM_FMT)
+    {
+      if (sc->device_flags[i] & CFSYNCHISULTRA)
       {
-        p->discenable |= (0x01 << i);
+        p->ultraenb |= mask;
       }
-      if (p->flags & AHC_NEWEEPROM_FMT)
+      else if (sc->device_flags[i] & CFNEWULTRAFORMAT)
       {
-        if (sc->device_flags[i] & CFSYNCHISULTRA)
+        if ( (sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03 )
         {
-          p->ultraenb |= (0x01 << i);
+          sc->device_flags[i] &= ~CFXFER;
+          sc->device_flags[i] |= CFSYNCHISULTRA;
+          p->ultraenb |= mask;
         }
       }
-      else if (sc->adapter_control & CFULTRAEN)
+    }
+    else if (sc->adapter_control & CFULTRAEN)
+    {
+      p->ultraenb |= mask;
+    }
+    if ( (sc->device_flags[i] & CFSYNCH) == 0)
+    {
+      sc->device_flags[i] &= ~CFXFER;
+      p->ultraenb &= ~mask;
+      p->transinfo[i].user_offset = 0;
+      p->transinfo[i].user_period = 0;
+      p->transinfo[i].cur_offset = 0;
+      p->transinfo[i].cur_period = 0;
+      p->needsdtr_copy &= ~mask;
+    }
+    else
+    {
+      if (p->features & AHC_ULTRA2)
       {
-        p->ultraenb |= (0x01 << i);
+        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
+        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
+        scsirate = (sc->device_flags[i] & CFXFER) |
+                   ((p->ultraenb & mask) ? 0x18 : 0x10);
+        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
+                                       AHC_SYNCRATE_ULTRA2);
+        p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                       aic_inb(p, TARG_SCSIRATE + i),
+                                       AHC_SYNCRATE_ULTRA2);
       }
-      if ( ((target_settings & 0x70) == 0x40) &&
-           (p->ultraenb & (0x01 << i)) )
+      else
       {
-        target_settings &= ~0x70;
-        p->ultraenb &= ~(0x01 << i);
+        scsirate = (sc->device_flags[i] & CFXFER) << 4;
+        if (sc->device_flags[i] & CFWIDEB)
+          p->transinfo[i].user_offset = MAX_OFFSET_16BIT;
+        else
+          p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
+        if (p->features & AHC_ULTRA)
+        {
+          short ultraenb;
+          ultraenb = aic_inb(p, ULTRA_ENB) |
+            (aic_inb(p, ULTRA_ENB + 1) << 8);
+          p->transinfo[i].user_period = aic7xxx_find_period(p,
+                                          scsirate,
+                                          (p->ultraenb & mask) ?
+                                          AHC_SYNCRATE_ULTRA :
+                                          AHC_SYNCRATE_FAST);
+          p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                         aic_inb(p, TARG_SCSIRATE + i),
+                                         (ultraenb & mask) ? 
+                                         AHC_SYNCRATE_ULTRA :
+                                         AHC_SYNCRATE_FAST);
+        }
+        else
+          p->transinfo[i].user_period = aic7xxx_find_period(p,
+                                          scsirate, AHC_SYNCRATE_FAST);
       }
-      /*
-       * Don't output these settings if we aren't resetting the bus, instead,
-       * leave the devices current settings in place
-       */
-      if (!(aic7xxx_no_reset))
-        aic_outb(p, target_settings, TARG_SCRATCH + i);
+      p->needsdtr_copy |= mask;
     }
-    aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
-    aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
-    aic_outb(p,  (p->ultraenb & 0xFF), ULTRA_ENB);
-    aic_outb(p,  ((p->ultraenb >> 8) & 0xFF), ULTRA_ENB + 1);
-
-    p->scsi_id = sc->brtime_id & CFSCSIID;
-    p->adapter_control = sc->adapter_control;
-    p->bios_control = sc->bios_control;
-
-    if (p->bios_control & CFBIOSEN)
+    if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) )
     {
-      p->flags &= ~AHC_USEDEFAULTS;
-      p->flags |= AHC_BIOS_ENABLED;
+      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_16_BIT;
+      p->needwdtr_copy |= mask;
     }
     else
     {
-      p->flags &= ~AHC_BIOS_ENABLED;
-      p->flags |= AHC_USEDEFAULTS;
+      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_8_BIT;
+      p->needwdtr_copy &= ~mask;
     }
+    p->transinfo[i].cur_width =
+      (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) ?
+      MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT;
+  }
+  aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
+  aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+  p->needwdtr = p->needwdtr_copy;
+  p->needsdtr = p->needsdtr_copy;
+  p->wdtr_pending = p->sdtr_pending = 0;
+
+  /*
+   * We set the p->ultraenb from the SEEPROM to begin with, but now we make
+   * it match what is already down in the card.  If we are doing a reset
+   * on the card then this will get put back to a default state anyway.
+   * This allows us to not have to pre-emptively negotiate when using the
+   * no_reset option.
+   */
+  if (p->features & AHC_ULTRA)
+    p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8);
+
+  
+  scsi_conf = (p->scsi_id & HSCSIID);
+
+  if(have_seeprom)
+  {
+    p->adapter_control = sc->adapter_control;
+    p->bios_control = sc->bios_control;
 
-    if ((p->type & 0x1ff1) == AHC_AIC7895)
+    switch (p->chip & AHC_CHIPID_MASK)
     {
-      if (p->adapter_control & CFBPRIMARY)
-        p->flags |= AHC_CHANNEL_B_PRIMARY;
+      case AHC_AIC7895:
+      case AHC_AIC7896:
+        if (p->adapter_control & CFBPRIMARY)
+          p->flags |= AHC_CHANNEL_B_PRIMARY;
+      default:
+        break;
     }
 
-    scsi_conf = (p->scsi_id & 0x7);
     if (sc->adapter_control & CFSPARITY)
       scsi_conf |= ENSPCHK;
-    /*
-     * The 7850 controllers with a seeprom, do not honor the CFRESETB
-     * flag in the seeprom.  Assume that we want to reset the SCSI bus.
-     */
-    if (sc->adapter_control & CFRESETB)
-      scsi_conf |= RESET_SCSI;
-    /*
-     * We may be a 2842, if so, preserve the TERM_ENB bit in scsi conf
-     */
-    if ( (p->flags & AHC_TERM_ENB_A) && 
-        ((p->type & AHC_AIC7770) == AHC_AIC7770) )
-      scsi_conf |= TERM_ENB;
-    /*
-     * If this is an Ultra card, is Ultra mode enabled?  If not, disable
-     * it in the host struct as well
-     */
-    if ( (p->type & AHC_ULTRA) && 
-        !(sc->adapter_control & CFULTRAEN) &&
-        !(p->flags & AHC_NEWEEPROM_FMT) )
-      p->type &= ~AHC_ULTRA;
+  }
+  else
+  {
+    scsi_conf |= ENSPCHK | RESET_SCSI;
+  }
 
+  /*
+   * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card.
+   * The 2842 and 2742 cards already have these registers set and we don't
+   * want to muck with them since we don't set all the bits they do.
+   */
+  if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
+  {
     /* Set the host ID */
     aic_outb(p, scsi_conf, SCSICONF);
     /* In case we are a wide card */
     aic_outb(p, p->scsi_id, SCSICONF + 1);
-
-    if ((p->type & AHC_AIC7860) == AHC_AIC7860)
-    {
-      if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
-      /*
-       * Update the settings in sxfrctl1 to match the termination
-       * settings.
-       */
-        configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
-    }
-    else if (have_seeprom && ((p->type & AHC_AIC7770) != AHC_AIC7770))
-      configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
   }
-  return (have_seeprom);
 }
 
 /*+F*************************************************************************
@@ -7146,8 +8508,10 @@
   struct aic7xxx_host *current_p = NULL;
   struct aic7xxx_host *list_p = NULL;
   int found = 0;
+#if defined(__i386__) || defined(__alpha__)
   ahc_flag_type flags = 0;
-  ahc_type type;
+  int type;
+#endif
   unsigned char sxfrctl1;
 #if defined(__i386__) || defined(__alpha__)
   unsigned char hcntrl, hostconf;
@@ -7181,7 +8545,7 @@
    * EISA/VL-bus card signature probe.
    */
   slot = MINSLOT;
-  while (slot <= MAXSLOT)
+  while ( (slot <= MAXSLOT) && !(aic7xxx_no_probe) )
   {
     base = SLOTBASE(slot) + MINREG;
 
@@ -7195,27 +8559,11 @@
       continue; /* back to the beginning of the for loop */
     }
     flags = 0;
-    type = aic7xxx_probe(slot, base + HID0, &flags);
-    switch (type)
+    type = aic7xxx_probe(slot, base + AHC_HID0, &flags);
+    if (type == -1)
     {
-      case AHC_AIC7770:
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at EISA %d\n",
-               board_names[2], slot);
-        break;
-      case AHC_274:
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at EISA %d\n",
-               board_names[3], slot);
-        break;
-      case AHC_284:
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at VLB %d\n",
-               board_names[4], slot);
-        break;
-      default:
-        slot++;
-        continue; /* back to the beginning of the while loop */
+      slot++;
+      continue;
     }
     temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
     if (temp_p == NULL)
@@ -7238,8 +8586,6 @@
     temp_p->unpause = hcntrl | INTEN;
     temp_p->pause = hcntrl | PAUSE | INTEN;
     temp_p->base = base;
-    temp_p->type = type;
-    temp_p->flags = flags | AHC_PAGESCBS;
     temp_p->mbase = 0;
     temp_p->maddr = 0;
     temp_p->pci_bus = 0;
@@ -7250,6 +8596,8 @@
       temp_p->irq = 0;
     else
       temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
+    temp_p->flags |= AHC_PAGESCBS;
+
     switch (temp_p->irq)
     {
       case 9:
@@ -7272,6 +8620,7 @@
      * We are commited now, everything has been checked and this card
      * has been found, now we just set it up
      */
+
     /*
      * Insert our new struct into the list at the end
      */
@@ -7293,12 +8642,18 @@
         temp_p->flags |= AHC_EXTEND_TRANS_B;
     }
 
-    switch (temp_p->type & 0x1ff1)
+    switch (type)
     {
-      case AHC_AIC7770:
+      case 0:
         temp_p->board_name_index = 2;
-      case AHC_274:
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("aic7xxx: <%s> at EISA %d\n",
+               board_names[2], slot);
+        /* FALLTHROUGH */
+      case 1:
       {
+        temp_p->chip = AHC_AIC7770 | AHC_EISA;
+        temp_p->features |= AHC_AIC7770_FE;
         temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL);
 
         /*
@@ -7307,7 +8662,12 @@
          * the mid-level SCSI code which channel is primary.
          */
         if (temp_p->board_name_index == 0)
+        {
           temp_p->board_name_index = 3;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at EISA %d\n",
+                 board_names[3], slot);
+        }
         if (temp_p->bios_control & CHANNEL_B_PRIMARY)
         {
           temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
@@ -7315,7 +8675,6 @@
 
         if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
         {
-          temp_p->flags |= AHC_USEDEFAULTS;
           temp_p->flags &= ~AHC_BIOS_ENABLED;
         }
         else
@@ -7324,79 +8683,46 @@
           temp_p->flags |= AHC_BIOS_ENABLED;
           if ( (temp_p->bios_control & 0x20) == 0 )
           {
-            switch(temp_p->bios_control & 0x07)
-            {
-              case 0x0:
-                temp_p->bios_address = 0xcc000;
-                break;
-              case 0x1:
-                temp_p->bios_address = 0xd0000;
-                break;
-              case 0x2:
-                temp_p->bios_address = 0xd4000;
-                break;
-              case 0x3:
-                temp_p->bios_address = 0xd8000;
-                break;
-              case 0x4:
-                temp_p->bios_address = 0xdc000;
-                break;
-              case 0x5:
-                temp_p->bios_address = 0xe0000;
-                break;
-              case 0x6:
-                temp_p->bios_address = 0xe4000;
-                break;
-              case 0x7:
-                temp_p->bios_address = 0xe8000;
-                break;
-              default:
-                break; /* can't get here */
-            }
+            temp_p->bios_address = 0xcc000;
+            temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07));
           }
           else
           {
-            switch(temp_p->bios_control & 0x06)
-            {
-              case 0x0:
-                temp_p->bios_address = 0xd0000;
-                break;
-              case 0x2:
-                temp_p->bios_address = 0xd8000;
-                break;
-              case 0x4:
-                temp_p->bios_address = 0xe0000;
-                break;
-              case 0x6:
-                temp_p->bios_address = 0xe8000;
-                break;
-              default:
-                break; /* can't get here */
-            }
+            temp_p->bios_address = 0xd0000;
+            temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06));
           }
         }
         temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
         temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
-        if (temp_p->flags & AHC_USEDEFAULTS)
+        if (temp_p->features & AHC_WIDE)
         {
-          temp_p->scsi_id = temp_p->scsi_id_b = 7;
-          temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
+          temp_p->scsi_id = temp_p->adapter_control & HWSCSIID;
+          temp_p->scsi_id_b = temp_p->scsi_id;
         }
         else
         {
-          if ( ((temp_p->adapter_control >> 8) & TERM_ENB) != 0 )
-            temp_p->flags |= AHC_TERM_ENB_A;
-          if ( (temp_p->adapter_control & TERM_ENB) != 0 )
-            temp_p->flags |= AHC_TERM_ENB_B;
           temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
           temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
         }
+        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
         break;
       }
 
-      case AHC_284:
-        load_seeprom(temp_p, &sxfrctl1);
+      case 2:
+      case 3:
+        temp_p->chip = AHC_AIC7770 | AHC_VL;
+        temp_p->features |= AHC_AIC7770_FE;
+        if (type == 2)
+          temp_p->flags |= AHC_BIOS_ENABLED;
+        else
+          temp_p->flags &= ~AHC_BIOS_ENABLED;
+        if (aic_inb(temp_p, SCSICONF) & TERM_ENB)
+          sxfrctl1 = STPWEN;
+        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
         temp_p->board_name_index = 4;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("aic7xxx: <%s> at VLB %d\n",
+               board_names[2], slot);
         switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
         {
           case 0x00:
@@ -7455,42 +8781,84 @@
     {
       unsigned short      vendor_id;
       unsigned short      device_id;
-      ahc_type            type;
+      ahc_chip            chip;
       ahc_flag_type       flags;
+      ahc_feature         features;
       int                 board_name_index;
-    } const aic7xxx_pci_devices[] = {
+      unsigned short      seeprom_size;
+      unsigned short      seeprom_type;
+    } const aic_pdevs[] = {
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
-       AHC_FNONE,                                            1 },
+       AHC_FNONE, AHC_FENONE,                                1,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
-       AHC_PAGESCBS | AHC_USEDEFAULTS,                       5 },
+       AHC_PAGESCBS, AHC_AIC7850_FE,                         5,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
-       AHC_PAGESCBS | AHC_USEDEFAULTS,                       6 },
+       AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,  7 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_294AU,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,  8 },
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       8,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                      9 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_294,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     10 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_394,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     11 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_398,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     12 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_294,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     13 },
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,      9,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     10,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7870_FE,                                      11,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7870_FE,                                      12,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     13,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     14 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_294U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     15 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_394U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     16 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_398U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     17 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_294U,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     18 },
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     14,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     15,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7880_FE,                                      16,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7880_FE,                                      17,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 19 }
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7895_FE,                                      19,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      20,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      21,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      22,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      23,
+       32, C56_66 },
     };
 
     unsigned short command;
@@ -7498,7 +8866,6 @@
 #ifdef MMAPIO
     unsigned long page_offset, base;
 #endif
-    struct aic7xxx_host *first_7895 = NULL;
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
     struct pci_dev *pdev = NULL;
 #else
@@ -7507,17 +8874,17 @@
     unsigned char pci_bus, pci_devfn, pci_irq;
 #endif
 
-    for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
+    for (i = 0; i < NUMBER(aic_pdevs); i++)
     {
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
       pdev = NULL;
-      while ((pdev = pci_find_device(aic7xxx_pci_devices[i].vendor_id,
-                                     aic7xxx_pci_devices[i].device_id,
+      while ((pdev = pci_find_device(aic_pdevs[i].vendor_id,
+                                     aic_pdevs[i].device_id,
                                      pdev)))
 #else
       index = 0;
-      while (!(pcibios_find_device(aic7xxx_pci_devices[i].vendor_id,
-                                   aic7xxx_pci_devices[i].device_id,
+      while (!(pcibios_find_device(aic_pdevs[i].vendor_id,
+                                   aic_pdevs[i].device_id,
                                    index++, &pci_bus, &pci_devfn)) )
 #endif
       {
@@ -7534,9 +8901,12 @@
                                     GFP_ATOMIC)) != NULL )
         {
           memset(temp_p, 0, sizeof(struct aic7xxx_host));
-          temp_p->type = aic7xxx_pci_devices[i].type;
-          temp_p->flags = aic7xxx_pci_devices[i].flags;
-          temp_p->board_name_index = aic7xxx_pci_devices[i].board_name_index;
+          temp_p->chip = aic_pdevs[i].chip | AHC_PCI;
+          temp_p->flags = aic_pdevs[i].flags;
+          temp_p->features = aic_pdevs[i].features;
+          temp_p->board_name_index = aic_pdevs[i].board_name_index;
+          temp_p->sc_size = aic_pdevs[i].seeprom_size;
+          temp_p->sc_type = aic_pdevs[i].seeprom_type;
 
           /*
            * Read sundry information from PCI BIOS.
@@ -7548,19 +8918,29 @@
           temp_p->pci_device_fn = pdev->devfn;
           temp_p->base = pdev->base_address[0];
           temp_p->mbase = pdev->base_address[1];
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(temp_p->pdev->devfn),
+              PCI_FUNC(temp_p->pdev->devfn));
           pci_read_config_word(pdev, PCI_COMMAND, &command);
           if (aic7xxx_verbose & VERBOSE_PROBE2)
           {
             printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
               (int)command);
           }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
             PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
             PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+          command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pci_write_config_word(pdev, PCI_COMMAND, command);
-          pci_read_config_dword(pdev, PCI_COMMAND, &devconfig);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+          pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
           if (aic7xxx_verbose & VERBOSE_PROBE2)
           {
             printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
@@ -7574,15 +8954,16 @@
           {
             devconfig |= 0x00000008;
           }
-          pci_write_config_dword(pdev, PCI_COMMAND, devconfig);
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk("aic7xxx: <%s> at PCI %d/%d\n", 
-              board_names[aic7xxx_pci_devices[i].board_name_index],
-              PCI_SLOT(temp_p->pdev->devfn),
-              PCI_FUNC(temp_p->pdev->devfn));
-#else
+          pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+#else  /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
           temp_p->pci_bus = pci_bus;
           temp_p->pci_device_fn = pci_devfn;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
           pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
             &pci_irq);
           temp_p->irq = pci_irq;
@@ -7598,12 +8979,17 @@
             printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
               (int)command);
           }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
           command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
             PCI_COMMAND_INVALIDATE | PCI_COMMAND_MASTER |
             PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+          command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
           if (aic7xxx_pci_parity == 0)
             command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
           pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
           pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig);
           if (aic7xxx_verbose & VERBOSE_PROBE2)
           {
@@ -7619,12 +9005,8 @@
             devconfig |= 0x00000008;
           }
           pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig);
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk("aic7xxx: <%s> at PCI %d/%d\n", 
-              board_names[aic7xxx_pci_devices[i].board_name_index],
-              PCI_SLOT(temp_p->pci_device_fn),
-              PCI_FUNC(temp_p->pci_device_fn));
-#endif
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+#endif /* LINUIX_VERSION_CODE > KERNEL_VERSION(2,1,92) */
 
           /*
            * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
@@ -7632,49 +9014,24 @@
            */
           temp_p->base &= PCI_BASE_ADDRESS_IO_MASK;
           temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
-          temp_p->unpause = (aic_inb(temp_p, HCNTRL) & IRQMS) | INTEN;
+          temp_p->unpause = INTEN;
           temp_p->pause = temp_p->unpause | PAUSE;
 
 #ifdef MMAPIO
-          if((temp_p->type & AHC_AIC7850) != AHC_AIC7850)
-          {
-            base = temp_p->mbase & PAGE_MASK;
-            page_offset = temp_p->mbase - base;
-            /*
-             * replace the next line with this one if you are using 2.1.x:
-             * temp_p->maddr = ioremap(base, page_offset + 256);
-             */
+          base = temp_p->mbase & PAGE_MASK;
+          page_offset = temp_p->mbase - base;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-            temp_p->maddr = ioremap(base, page_offset + 256);
+          temp_p->maddr = ioremap_nocache(base, page_offset + 256);
 #else
-            temp_p->maddr = vremap(base, page_offset + 256);
+          temp_p->maddr = vremap(base, page_offset + 256);
 #endif
-            if(temp_p->maddr)
-            {
-              temp_p->maddr += page_offset;
-            }
-          }
-          else
+          if(temp_p->maddr)
           {
-#ifdef __i386__
-            /*
-             * Resort to PIO mode on these controllers and Intel hardware.
-             * For other hardware we need to either disable these controllers
-             * or do without MMAPed IO.  However, for PPC, we can't do
-             * MMAPed IO (according to what I've heard) so we may be forced
-             * to just fail detection on those cards.
-             */
-            temp_p->maddr = NULL;
-#else
-            kfree(temp_p);
-            temp_p = NULL;
-            continue;
-#endif /* __i386__ */
+            temp_p->maddr += page_offset;
           }
 #endif
 
-          aic_outb(temp_p, temp_p->pause, HCNTRL);
-          while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+          pause_sequencer(temp_p);
 
           /*
            * Clear out any pending PCI error status messages.  Also set
@@ -7691,7 +9048,10 @@
           /*
            * Remember how the card was setup in case there is no seeprom.
            */
-          temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
+          if (temp_p->features & AHC_ULTRA2)
+            temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID;
+          else
+            temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
           /*
            * Get current termination setting
            */
@@ -7704,11 +9064,17 @@
             continue;
           }
 
-          switch (temp_p->type & 0x1ff1)
+          /*
+           * Doing a switch based upon i is really gross, but since Justin
+           * changed around the chip ID stuff, we can't use that any more.
+           * Since we don't scan the devices the same way as FreeBSD, we end
+           * up doing this gross hack in order to avoid totally splitting
+           * away from Justin's init code in ahc_pci.c
+           */
+          switch (i)
           {
-            case AHC_394:   /* 3940 */
-            case AHC_394U:  /* 3940-Ultra */
-              temp_p->flags |= AHC_MULTI_CHANNEL;
+            case 7:   /* 3940 */
+            case 12:  /* 3940-Ultra */
               switch(PCI_SLOT(temp_p->pci_device_fn))
               {
                 case 5:
@@ -7719,9 +9085,8 @@
               }
               break;
 
-            case AHC_398:   /* 3985 */
-            case AHC_398U:  /* 3985-Ultra */
-              temp_p->flags |= AHC_MULTI_CHANNEL;
+            case 8:   /* 3985 */
+            case 13:  /* 3985-Ultra */
               switch(PCI_SLOT(temp_p->pci_device_fn))
               {
                 case 8:
@@ -7735,54 +9100,44 @@
               }
               break;
 
-            case AHC_AIC7895:
-              temp_p->flags |= AHC_MULTI_CHANNEL;
+            case 15:
+            case 18:
+            case 19:
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
                 temp_p->flags |= AHC_CHNLB;
               }
-              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
-              devconfig = le32_to_cpu(devconfig);
-              devconfig |= SCBSIZE32;
-              devconfig = cpu_to_le32(devconfig);
-              pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+              /*
+               * The 7895 is the only chipset that sets the SCBSIZE32 param
+               * in the DEVCONFIG register.  The Ultra2 chipsets use
+               * the DSCOMMAND0 register instead.
+               */
+              if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+              {
+                pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+                devconfig |= SCBSIZE32;
+                pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+              }
 #else
               if (PCI_FUNC(temp_p->pci_device_fn) != 0)
               {
                 temp_p->flags |= AHC_CHNLB;
               }
-              pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
-                &devconfig);
-              devconfig |= SCBSIZE32;
-              pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
-                devconfig);
-#endif
-              if (aic7xxx_7895_irq_hack != -1)
+              /*
+               * The 7895 is the only chipset that sets the SCBSIZE32 param
+               * in the DEVCONFIG register.  The Ultra2 chipsets use
+               * the DSCOMMAND0 register instead.
+               */
+              if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
               {
-                if (first_7895 == NULL)
-                {
-                  printk(KERN_INFO "aic7xxx: Using 7895_irq_hack.  Please "
-                    "upgrade your motherboard BIOS\n");
-                  first_7895 = temp_p;
-                }
-                else if (aic7xxx_7895_irq_hack == 0)
-                {
-                  if (temp_p->flags & AHC_CHNLB)
-                    temp_p->irq = first_7895->irq;
-                  else
-                    first_7895->irq = temp_p->irq;
-                  first_7895 = NULL;
-                }
-                else
-                {
-                  if ( !(temp_p->flags & AHC_CHNLB) )
-                    temp_p->irq = first_7895->irq;
-                  else
-                    first_7895->irq = temp_p->irq;
-                  first_7895 = NULL;
-                }
+                pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                  &devconfig);
+                devconfig |= SCBSIZE32;
+                pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                  devconfig);
               }
+#endif
               break;
             default:
               break;
@@ -7794,21 +9149,95 @@
            * on 394x and 398x cards we'll end up reading the wrong settings
            * for channels B and C
            */
-          if ( !(load_seeprom(temp_p, &sxfrctl1)) )
+          switch (temp_p->chip & AHC_CHIPID_MASK)
           {
-            temp_p->flags |= AHC_USEDEFAULTS;
-            if (sxfrctl1 & STPWEN)
-              temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
+            case AHC_AIC7890:
+            case AHC_AIC7896:
+              aic_outb(temp_p, 0, SCAMCTL);
+              /*
+               * We used to set DPARCKEN in this register, but after talking
+               * to a tech from Adaptec, I found out they don't use that
+               * particular bit in their own register settings, and when you
+               * combine that with the fact that I determined that we were
+               * seeing Data-Path Parity Errors on things we shouldn't see
+               * them on, I think there is a bug in the silicon and the way
+               * to work around it is to disable this particular check.  Also
+               * This bug only showed up on certain commands, so it seems to
+               * be pattern related or some such.  The commands we would
+               * typically send as a linux TEST_UNIT_READY or INQUIRY command
+               * could cause it to be triggered, while regular commands that
+               * actually made reasonable use of the SG array capabilities
+               * seemed not to cause the problem.
+               */
+              /*
+              aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) |
+                               CACHETHEN | DPARCKEN | MPARCKEN |
+                               USCBSIZE32 | CIOPARCKEN,
+                               DSCOMMAND0);
+               */
+              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                CACHETHEN | MPARCKEN | USCBSIZE32 |
+                                CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
+              /* FALLTHROUGH */
+            default:
+              /*
+               * We attempt to read a SEEPROM on *everything*.  If we fail,
+               * then we fail, but this covers things like 2910c cards that
+               * now have SEEPROMs with their 7856 chipset that we would
+               * otherwise ignore.  They still don't have a BIOS, but they
+               * have a SEEPROM that the SCSISelect utility on the Adaptec
+               * diskettes can configure.
+               */
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7850:
+            case AHC_AIC7860:
+              /*
+               * Set the DSCOMMAND0 register on these cards different from
+               * on the 789x cards.  Also, read the SEEPROM as well.
+               */
+              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                CACHETHEN | MPARCKEN) & ~DPARCKEN,
+                       DSCOMMAND0);
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7880:
+              /*
+               * Only set the DSCOMMAND0 register if this is a Rev B.
+               * chipset.  For those, we also enable Ultra mode by
+               * force due to brain-damage on the part of some BIOSes
+               * We overload the devconfig variable here since we can.
+               */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+#else
+              pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                                        &devconfig);
+#endif
+              if ((devconfig & 0xff) >= 1)
+              {
+                aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                  CACHETHEN | MPARCKEN) & ~DPARCKEN,
+                         DSCOMMAND0);
+              }
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
           }
+          
 
           /*
            * and then we need another switch based on the type in order to
            * make sure the channel B primary flag is set properly on 7895
-           * controllers....Arrrgggghhh!!!
+           * controllers....Arrrgggghhh!!!  We also have to catch the fact
+           * that when you disable the BIOS on the 7895 on the Intel DK440LX
+           * motherboard, and possibly others, it only sets the BIOS disabled
+           * bit on the A channel...I think I'm starting to lean towards
+           * going postal....
            */
-          switch(temp_p->type & 0x1ff1)
+          switch(temp_p->chip & AHC_CHIPID_MASK)
           {
             case AHC_AIC7895:
+            case AHC_AIC7896:
               current_p = list_p;
               while(current_p != NULL)
               {
@@ -7817,11 +9246,21 @@
                       PCI_SLOT(temp_p->pci_device_fn)) )
                 {
                   if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
+                  {
                     temp_p->flags |= 
                       (current_p->flags & AHC_CHANNEL_B_PRIMARY);
+                    temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+                    temp_p->flags |=
+                      (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+                  }
                   else
+                  {
                     current_p->flags |=
                       (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
+                    current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+                    current_p->flags |=
+                      (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+                  }
                 }
                 current_p = current_p->next;
               }
@@ -7831,6 +9270,56 @@
           }
 
           /*
+           * We do another switch based on i so that we can exclude all
+           * 3895 devices from the next option since the 3895 cards use
+           * shared external SCB RAM while all other cards have dedicated
+           * external SCB RAM per channel.  Also exclude the 7850 and
+           * 7860 based stuff since they can have garbage in the bit
+           * that indicates external RAM and get some of this stuff
+           * wrong as a result.
+           */
+          switch(temp_p->chip & AHC_CHIPID_MASK)
+          {
+            default:
+              break;
+            case AHC_AIC7895:
+            case AHC_AIC7896:
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+#else
+              pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                                        &devconfig);
+#endif
+              if (temp_p->features & AHC_ULTRA2)
+              {
+                if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2)
+                {
+                  aic_outb(temp_p,
+                           aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2,
+                           DSCOMMAND0);
+                  temp_p->flags |= AHC_EXTERNAL_SRAM;
+                  devconfig |= EXTSCBPEN;
+                }
+              }
+              else if (devconfig & RAMPSM)
+              {
+                devconfig &= ~SCBRAMSEL;
+                devconfig |= EXTSCBPEN;
+                temp_p->flags |= AHC_EXTERNAL_SRAM;
+              }
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+              pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#else
+              pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                                         devconfig);
+#endif
+              if ( (temp_p->flags & AHC_EXTERNAL_SRAM) &&
+                   (temp_p->flags & AHC_CHNLB) )
+                aic_outb(temp_p, 1, CCSCBBADDR);
+              break;
+          }
+
+          /*
            * Take the LED out of diagnostic mode
            */
           aic_outb(temp_p, 
@@ -7839,75 +9328,21 @@
 
           /*
            * We don't know where this is set in the SEEPROM or by the
-           * BIOS, so we default to 100%.
+           * BIOS, so we default to 100%.  On Ultra2 controllers, use 75%
+           * instead.
            */
-          aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
-
-          if (temp_p->flags & AHC_USEDEFAULTS)
+          if (temp_p->features & AHC_ULTRA2)
           {
-            int j;
-            unsigned char k;
-            /*
-             * 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++)
-            {
-              k = aic_inb(temp_p, j);
-              /* Check for all zeros and ones.  Break out if we pass */
-              if( (k != 0x00) && (k != 0xff) )
-                break;
-            }
-            /* If j makes it to 0x60, then all entries are either 0x00 or
-             * 0xff.  We would then assume we have *not* been initialized
-             * and drop through here.  OTOH, if even one entry is inited,
-             * then as long as we appear to have a valid SCSI ID, we'll use
-             * the leftover BIOS values.
-             */
-            if ((j != 0x60) && (temp_p->scsi_id != 0))
-            {
-              temp_p->flags &= ~AHC_USEDEFAULTS;
-              if (aic7xxx_verbose & VERBOSE_PROBE2)
-              {
-                printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
-              }
-            }
-            else
-            {
-              /*
-               * Assume only one connector and always turn on
-               * termination.
-               */
-              temp_p->flags &= ~AHC_BIOS_ENABLED;
-              temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
-              sxfrctl1 = STPWEN;
-              temp_p->scsi_id = 7;
-            }
-            aic_outb(temp_p, (temp_p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
-                 SCSICONF);
-            /* In case we are a wide card. */
-            aic_outb(temp_p, temp_p->scsi_id, SCSICONF + 1);
+            aic_outb(temp_p, RD_DFTHRSH_75 | WR_DFTHRSH_75, DFF_THRSH);
           }
-          else /* not using defaults */
+          else
           {
-            if (sxfrctl1 & STPWEN)
-              temp_p->flags |= AHC_TERM_ENB_A;
+            aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
           }
 
           if (aic7xxx_extended)
             temp_p->flags |= AHC_EXTEND_TRANS_A;
 
-          /*
-           * Put our termination setting into sxfrctl1 now that the
-           * generic initialization is complete.
-           */
-          sxfrctl1 |= aic_inb(temp_p, SXFRCTL1);
-          aic_outb(temp_p, sxfrctl1, SXFRCTL1);
           if ( list_p == NULL )
           {
             list_p = current_p = temp_p;
@@ -7925,7 +9360,7 @@
         else /* Well, we found one, but we couldn't get any memory */
         {
           printk("aic7xxx: Found <%s>\n", 
-            board_names[aic7xxx_pci_devices[i].board_name_index]);
+            board_names[aic_pdevs[i].board_name_index]);
           printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
             "skipping.\n");
         }
@@ -7946,87 +9381,74 @@
    */
 
   {
-    struct aic7xxx_host *vlb_enab, *vlb_disab, *pci;
+    struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL };
+    struct aic7xxx_host *vlb, *pci;
     struct aic7xxx_host *prev_p;
     struct aic7xxx_host *p;
     unsigned char left;
 
-    prev_p = vlb_enab = vlb_disab = pci = NULL;
+    prev_p = vlb = pci = NULL;
 
     temp_p = list_p;
     while (temp_p != NULL)
     {
-      switch(temp_p->type)
+      switch(temp_p->chip & ~AHC_CHIPID_MASK)
       {
-        case AHC_AIC7770:
-        case AHC_274:
-        case AHC_284:
-          if (temp_p->flags & AHC_BIOS_ENABLED)
+        case AHC_EISA:
+        case AHC_VL:
+        {
+          p = temp_p;
+          if (p->flags & AHC_BIOS_ENABLED)
+            vlb = sort_list[0];
+          else
+            vlb = sort_list[2];
+
+          if (vlb == NULL)
           {
-            if (vlb_enab == NULL)
-            {
-              vlb_enab = temp_p;
-              temp_p = temp_p->next;
-              vlb_enab->next = NULL;
-            }
-            else
-            {
-              current_p = vlb_enab;
-              prev_p = NULL;
-              while ( (current_p != NULL) &&
-                      (current_p->bios_address < temp_p->bios_address))
-              {
-                prev_p = current_p;
-                current_p = current_p->next;
-              }
-              if (prev_p != NULL)
-              {
-                prev_p->next = temp_p;
-                temp_p = temp_p->next;
-                prev_p->next->next = current_p;
-              }
-              else
-              {
-                vlb_enab = temp_p;
-                temp_p = temp_p->next;
-                vlb_enab->next = current_p;
-              }
-            }
+            vlb = temp_p;
+            temp_p = temp_p->next;
+            vlb->next = NULL;
           }
           else
           {
-            if (vlb_disab == NULL)
+            current_p = vlb;
+            prev_p = NULL;
+            while ( (current_p != NULL) &&
+                    (current_p->bios_address < temp_p->bios_address))
+            {
+              prev_p = current_p;
+              current_p = current_p->next;
+            }
+            if (prev_p != NULL)
             {
-              vlb_disab = temp_p;
+              prev_p->next = temp_p;
               temp_p = temp_p->next;
-              vlb_disab->next = NULL;
+              prev_p->next->next = current_p;
             }
             else
             {
-              current_p = vlb_disab;
-              prev_p = NULL;
-              while ( (current_p != NULL) &&
-                      (current_p->base < temp_p->base))
-              {
-                prev_p = current_p;
-                current_p = current_p->next;
-              }
-              if (prev_p != NULL)
-              {
-                prev_p->next = temp_p;
-                temp_p = temp_p->next;
-                prev_p->next->next = current_p;
-              }
-              else
-              {
-                vlb_disab = temp_p;
-                temp_p = temp_p->next;
-                vlb_disab->next = current_p;
-              }
+              vlb = temp_p;
+              temp_p = temp_p->next;
+              vlb->next = current_p;
             }
           }
+          
+          if (p->flags & AHC_BIOS_ENABLED)
+            sort_list[0] = vlb;
+          else
+            sort_list[2] = vlb;
+          
           break;
+        }
         default:  /* All PCI controllers fall through to default */
+        {
+
+          p = temp_p;
+          if (p->flags & AHC_BIOS_ENABLED) 
+            pci = sort_list[1];
+          else
+            pci = sort_list[3];
+
           if (pci == NULL)
           {
             pci = temp_p;
@@ -8101,75 +9523,60 @@
               pci->next = current_p;
             }
           }
+
+          if (p->flags & AHC_BIOS_ENABLED)
+            sort_list[1] = pci;
+          else
+            sort_list[3] = pci;
+
           break;
+        }
       }  /* End of switch(temp_p->type) */
     } /* End of while (temp_p != NULL) */
     /*
      * At this point, the cards have been broken into 4 sorted lists, now
      * we run through the lists in order and register each controller
      */
-    left = found;
-    temp_p = vlb_enab;
-    while(temp_p != NULL)
-    {
-      template->name = board_names[temp_p->board_name_index];
-      p = aic7xxx_alloc(template, temp_p);
-      if (p != NULL)
-      {
-        p->instance = found - left;
-        if (aic7xxx_register(template, p, (--left)) == 0)
-        {
-          found--;
-          aic7xxx_free(p);
-          scsi_unregister(p->host);
-        }
-      }
-      current_p = temp_p;
-      temp_p = (struct aic7xxx_host *)temp_p->next;
-      kfree(current_p);
-    }
-    temp_p = pci;
-    while(temp_p != NULL)
-    {
-      template->name = board_names[temp_p->board_name_index];
-      p = aic7xxx_alloc(template, temp_p);
-      if (p != NULL)
-      {
-        p->instance = found - left;
-        if (aic7xxx_register(template, p, (--left)) == 0)
-        {
-          found--;
-          aic7xxx_free(p);
-          scsi_unregister(p->host);
-        }
-      }
-      current_p = temp_p;
-      temp_p = (struct aic7xxx_host *)temp_p->next;
-      kfree(current_p);
-    }
-    temp_p = vlb_disab;
-    while(temp_p != NULL)
     {
-      template->name = board_names[temp_p->board_name_index];
-      p = aic7xxx_alloc(template, temp_p);
-      if (p != NULL)
+      int i;
+      
+      left = found;
+      for (i=0; i<NUMBER(sort_list); i++)
       {
-        p->instance = found - left;
-        if (aic7xxx_register(template, p, (--left)) == 0)
+        temp_p = sort_list[i];
+        while(temp_p != NULL)
         {
-          found--;
-          aic7xxx_free(p);
-          scsi_unregister(p->host);
+          template->name = board_names[temp_p->board_name_index];
+          p = aic7xxx_alloc(template, temp_p);
+          if (p != NULL)
+          {
+            p->instance = found - left;
+            if (aic7xxx_register(template, p, (--left)) == 0)
+            {
+              found--;
+              aic7xxx_release(p->host);
+              scsi_unregister(p->host);
+            }
+            else if (aic7xxx_dump_card)
+            {
+              pause_sequencer(p);
+              aic7xxx_print_card(p);
+              aic7xxx_print_scratch_ram(p);
+              unpause_sequencer(p, TRUE);
+            }
+          }
+          current_p = temp_p;
+          temp_p = (struct aic7xxx_host *)temp_p->next;
+          kfree(current_p);
         }
       }
-      current_p = temp_p;
-      temp_p = (struct aic7xxx_host *)temp_p->next;
-      kfree(current_p);
     }
   }
   return (found);
 }
 
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_negotiation_complete
@@ -8181,7 +9588,7 @@
 static void
 aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
 {
-  memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer));
+  return;
 }
 
 /*+F*************************************************************************
@@ -8221,10 +9628,19 @@
       cmd->underflow = 0;
       cmd->cmd_len = 6;
     }
+    /*
+     * Before sending this thing out, we also amke the cmd->next pointer
+     * point to the real command so we can stuff any possible SENSE data
+     * intp the real command instead of this fake command.  This has to be
+     * done each time the command is built, not just the first time, hence
+     * it's outside of the above if()...
+     */
+    p->dev_wdtr_cmnd[tindex]->next = old_cmd;
     aic7xxx_queue(p->dev_wdtr_cmnd[tindex], 
                   aic7xxx_negotiation_complete);
   }
-  else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) )
+  else if ( (p->needsdtr & (1<<tindex)) && !(p->sdtr_pending & (1<<tindex)) &&
+            !(p->wdtr_pending & (1<<tindex)) )
   {
     if(p->dev_sdtr_cmnd[tindex] == NULL)
     {
@@ -8248,11 +9664,45 @@
       cmd->underflow = 0;
       cmd->cmd_len = 6;
     }
+    /*
+     * Before sending this thing out, we also amke the cmd->next pointer
+     * point to the real command so we can stuff any possible SENSE data
+     * intp the real command instead of this fake command.  This has to be
+     * done each time the command is built, not just the first time, hence
+     * it's outside of the above if()...
+     */
+    p->dev_sdtr_cmnd[tindex]->next = old_cmd;
     aic7xxx_queue(p->dev_sdtr_cmnd[tindex], 
                   aic7xxx_negotiation_complete);
   }
 }
 
+#endif
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_scb
+ *
+ * Description:
+ *   Dump the byte codes for an about to be sent SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int i;
+  unsigned char *x;  
+
+  x = (unsigned char *)&scb->hscb->control;
+
+  for(i=0; i<32; i++)
+  {
+    printk("%02x ", x[i]);
+  }
+  printk("\n");
+}
+#endif
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_buildscb
@@ -8306,12 +9756,13 @@
   }
   if (p->dev_flags[TARGET_INDEX(cmd)] & DEVICE_SCANNED)
   {
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
     if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) )
     {
       if (cmd == p->dev_wdtr_cmnd[TARGET_INDEX(cmd)])
       {
         p->wdtr_pending |= mask;
-        scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+        scb->flags |= SCB_MSGOUT_WDTR;
         hscb->control &= DISCENB;
         hscb->control |= MK_MESSAGE;
         scb->tag_action = 0;
@@ -8321,7 +9772,8 @@
         aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
       }
     }
-    if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) )
+    else if ( (p->needsdtr & mask) && !(p->sdtr_pending & mask) &&
+              !(p->wdtr_pending & mask) )
     {
       if (cmd == p->dev_sdtr_cmnd[TARGET_INDEX(cmd)])
       {
@@ -8336,6 +9788,36 @@
         aic7xxx_build_negotiation_cmnd(p, cmd, TARGET_INDEX(cmd));
       }
     }
+#else
+    if ( (p->needwdtr & mask) && !(p->wdtr_pending & mask) &&
+         !(p->sdtr_pending & mask) && (cmd->lun == 0) )
+    {
+      p->wdtr_pending |= mask;
+      scb->flags |= SCB_MSGOUT_WDTR;
+      hscb->control &= DISCENB;
+      hscb->control |= MK_MESSAGE;
+      scb->tag_action = 0;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Building WDTR command.\n", p->host_no,
+               CTL_OF_CMD(cmd));
+#endif
+    }
+    else if ( (p->needsdtr & mask) && !(p->wdtr_pending & mask) &&
+              !(p->sdtr_pending & mask) && (cmd->lun == 0) )
+    {
+      p->sdtr_pending |= mask;
+      scb->flags |= SCB_MSGOUT_SDTR;
+      hscb->control &= DISCENB;
+      hscb->control |= MK_MESSAGE;
+      scb->tag_action = 0;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Building SDTR command.\n", p->host_no,
+               CTL_OF_CMD(cmd));
+#endif
+    }
+#endif
   }
   hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
         ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
@@ -8368,19 +9850,26 @@
 
     sg = (struct scatterlist *)cmd->request_buffer;
     scb->sg_length = 0;
+    /*
+     * Copy the segments into the SG array.  NOTE!!! - We used to
+     * have the first entry both in the data_pointer area and the first
+     * SG element.  That has changed somewhat.  We still have the first
+     * entry in both places, but now we download the address of
+     * scb->sg_list[1] instead of 0 to the sg pointer in the hscb.
+     */
     for (i = 0; i < cmd->use_sg; i++)
     {
       scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address));
       scb->sg_list[i].length = cpu_to_le32(sg[i].length);
       scb->sg_length += sg[i].length;
     }
-    hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(scb->sg_list));
-    hscb->SG_segment_count = cmd->use_sg;
-    scb->sg_count = cmd->use_sg;
-
     /* Copy the first SG into the data pointer area. */
     hscb->data_pointer = scb->sg_list[0].address;
     hscb->data_count = scb->sg_list[0].length;
+    scb->sg_count = cmd->use_sg;
+    hscb->SG_segment_count = cmd->use_sg;
+    hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[1]));
+
   }
   else
   {
@@ -8405,6 +9894,12 @@
       hscb->data_pointer = 0;
     }
   }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if((cmd->cmnd[0] == TEST_UNIT_READY) && (aic7xxx_verbose & VERBOSE_PROBE2))
+  {
+    aic7xxx_print_scb(p, scb);
+  }
+#endif
 }
 
 /*+F*************************************************************************
@@ -8419,7 +9914,9 @@
 {
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
   int tindex = TARGET_INDEX(cmd);
+#endif
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
   unsigned long cpu_flags = 0;
 #endif
@@ -8428,18 +9925,22 @@
   /*
    * Check to see if channel was scanned.
    */
+  
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
   if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
   {
-    printk(INFO_LEAD "Scanning channel for devices.\n",
-      p->host_no, 0, -1, -1);
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+      printk(INFO_LEAD "Scanning channel for devices.\n",
+        p->host_no, 0, -1, -1);
     p->flags |= AHC_A_SCANNED;
   }
   else
   {
     if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1))
     {
-      printk(INFO_LEAD "Scanning channel for devices.\n",
-        p->host_no, 1, -1, -1);
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(INFO_LEAD "Scanning channel for devices.\n",
+          p->host_no, 1, -1, -1);
       p->flags |= AHC_B_SCANNED;
     }
   }
@@ -8453,12 +9954,25 @@
     if ( p->dev_active_cmds[tindex] > 220 )
       p->dev_active_cmds[tindex] = 0;
   }
-  DRIVER_LOCK
-  scb = aic7xxx_allocate_scb(p, FALSE);
-  DRIVER_UNLOCK
+#endif
+
+  scb = scbq_remove_head(&p->scb_data->free_scbs);
+  if (scb == NULL)
+  {
+    DRIVER_LOCK
+    aic7xxx_allocate_scb(p);
+    DRIVER_UNLOCK
+    scb = scbq_remove_head(&p->scb_data->free_scbs);
+  }
   if (scb == NULL)
   {
-    panic("(scsi%d) aic7xxx_queue:Couldn't get a free SCB.\n", p->host_no);
+    printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no,
+           CTL_OF_CMD(cmd));
+    cmd->result = (DID_BUS_BUSY << 16);
+    DRIVER_LOCK
+    aic7xxx_queue_cmd_complete(p, cmd);
+    DRIVER_UNLOCK
+    return 0;
   }
   else
   {
@@ -8477,23 +9991,16 @@
      * the SCB to the sequencer and watch the fun begin.
      */
     cmd->scsi_done = fn;
+    cmd->result = DID_OK;
+    memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
     aic7xxx_error(cmd) = DID_OK;
     aic7xxx_status(cmd) = 0;
-    cmd->result = 0;
     cmd->host_scribble = NULL;
-    memset(&cmd->sense_buffer[0], 0, sizeof(cmd->sense_buffer));
 
     scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
     DRIVER_LOCK
-    if (p->delayed_scbs[tindex].head != NULL) 
-    {
-        scbq_insert_tail(&p->delayed_scbs[tindex], scb);
-    }
-    else
-    {
-        scbq_insert_tail(&p->waiting_scbs, scb);
-    }
+    scbq_insert_tail(&p->waiting_scbs, scb);
     if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
     {
       aic7xxx_run_waiting_queues(p);
@@ -8681,7 +10188,10 @@
       printk(INFO_LEAD "Queueing device reset "
            "command.\n", p->host_no, CTL_OF_SCB(scb));
     p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
     scb->flags |= SCB_QUEUED_ABORT;
     result = SCSI_RESET_PENDING;
   }
@@ -8702,30 +10212,142 @@
  *   Abort the current SCSI command(s).
  *-F*************************************************************************/
 void
-aic7xxx_panic_abort(struct aic7xxx_host *p)
+aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  int i;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+  int i, mask, found, need_tag;
+  struct aic7xxx_scb *scb;
+  unsigned char qinpos, hscbp;
 
-  printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION);
+  found = FALSE;
+#endif
+
+  printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION,
+         UTS_RELEASE);
   printk("Controller type:\n    %s\n", board_names[p->board_name_index]);
+  printk("p->flags=0x%x, p->chip=0x%x, p->features=0x%x, "
+         "sequencer %s paused\n",
+     p->flags, p->chip, p->features,
+    (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
+  pause_sequencer(p);
+  disable_irq(p->irq);
+  aic7xxx_print_card(p);
+  aic7xxx_print_scratch_ram(p);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
   for(i=0; i<MAX_TARGETS; i++)
   {
     if(p->dev_flags[i] & DEVICE_PRESENT)
     {
-      printk(INFO_LEAD "dev_flags=0x%x, WDTR:%s, SDTR:%s, q_depth=%d:%d\n",
+      mask = (0x01 << i);
+      printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c,"
+             " q_depth=%d:%d:%d\n",
         p->host_no, 0, i, 0, p->dev_flags[i],
-        (p->needwdtr_copy & (1 << i)) ? "Yes" : "No",
-        (p->needsdtr_copy & (1 << i)) ? "Yes" : "No",
+        (p->wdtr_pending & mask) ? 'Y' : 'N',
+        (p->needwdtr & mask) ? 'Y' : 'N',
+        (p->needwdtr_copy & mask) ? 'Y' : 'N',
+        (p->sdtr_pending & mask) ? 'Y' : 'N',
+        (p->needsdtr & mask) ? 'Y' : 'N',
+        (p->needsdtr_copy & mask) ? 'Y' : 'N',
+        p->dev_active_cmds[i],
         p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]);
+      printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0,
+             aic_inb(p, TARG_SCSIRATE + i));
+      if (p->features & AHC_ULTRA2)
+        printk(", targ_offset=%d", aic_inb(p, TARG_OFFSET + i));
+      printk("\n");
     }
   }
-  printk("SIMODE0=0x%x, SIMODE1=0x%x, SSTAT0=0x%x, SSTAT1=0x%x, INTSTAT=0x%x\n",
-    aic_inb(p, SIMODE0), aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
-    aic_inb(p, SSTAT1), aic_inb(p, INTSTAT) );
-  printk("p->flags=0x%x, p->type=0x%x, sequencer %s paused\n",
-     p->flags, p->type,
-    (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
-  panic("Stopping to debug\n");
+  /*
+   * Search for this command and see if we can't track it down, it's the
+   * one causing the timeout.  Print out this command first, then all other
+   * active commands afterwords.
+   */
+  need_tag = -1;
+  if ( cmd )
+  {
+    scb = p->scb_data->scb_array[aic7xxx_position(cmd)];
+    if ( (scb->flags & SCB_ACTIVE) && (scb->cmd == cmd) )
+    {
+      printk("Timed out command is scb #%d:\n", scb->hscb->tag);
+      printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag,
+             scb->flags, scb->hscb->control, scb->hscb->target_channel_lun,
+             (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" );
+      need_tag = scb->hscb->tag;
+      if (scb->flags & SCB_WAITINGQ) found=TRUE;
+    }
+  }
+  printk("QINFIFO: (TAG) ");
+  qinpos = aic_inb(p, QINPOS);
+  while ( qinpos != p->qinfifonext )
+  {
+    if (p->qinfifo[qinpos] == need_tag)
+      found=TRUE;
+    printk("%d ", p->qinfifo[qinpos++]);
+  }  
+  printk("\n");
+  printk("Current SCB: (SCBPTR/TAG/CONTROL) %d/%d/0x%x\n", aic_inb(p, SCBPTR),
+         aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL) );
+  if (aic_inb(p, SCB_TAG) == need_tag)  found=TRUE;
+  printk("WAITING_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+         hscbp = aic_inb(p, WAITING_SCBH));
+  while (hscbp != SCB_LIST_NULL)
+  {
+    aic_outb(p, hscbp, SCBPTR);
+    printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+    hscbp = aic_inb(p, SCB_NEXT);
+    if (aic_inb(p, SCB_TAG) == need_tag)  found=TRUE;
+  }
+  printk("\n");
+  printk("DISCONNECTED_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+         hscbp = aic_inb(p, DISCONNECTED_SCBH));
+  while (hscbp != SCB_LIST_NULL)
+  {
+    aic_outb(p, hscbp, SCBPTR);
+    printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+    hscbp = aic_inb(p, SCB_NEXT);
+    if (aic_inb(p, SCB_TAG) == need_tag)  found=TRUE;
+  }
+  printk("\n");
+  printk("FREE_SCBS: (SCBPTR/TAG/CONTROL) %d->",
+         hscbp = aic_inb(p, FREE_SCBH));
+  while (hscbp != SCB_LIST_NULL)
+  {
+    aic_outb(p, hscbp, SCBPTR);
+    printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL));
+    hscbp = aic_inb(p, SCB_NEXT);
+  }
+  printk("\n");
+
+  if (found == FALSE)
+  {
+    /*
+     * We haven't found the offending SCB yet, and it should be around
+     * somewhere, so go look for it in the cards SCBs.
+     */
+    printk("SCBPTR CONTROL TAG PREV NEXT\n");
+    for(i=0; i<p->scb_data->maxhscbs; i++)
+    {
+      aic_outb(p, i, SCBPTR);
+      printk("   %3d      %02x  %02x   %02x   %02x\n", i,
+             aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG),
+             aic_inb(p, SCB_PREV), aic_inb(p, SCB_NEXT));
+    }
+  }
+  
+
+  for (i=0; i < p->scb_data->numscbs; i++)
+  {
+    scb = p->scb_data->scb_array[i];
+    if ( (scb->flags & SCB_ACTIVE) && (scb->cmd != cmd) )
+    {
+      printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag,
+             scb->flags, scb->hscb->control, scb->hscb->target_channel_lun,
+             (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" );
+    }
+  }
+#endif
+  sti();
+  for(;;) barrier();
 }
 
 /*+F*************************************************************************
@@ -8759,7 +10381,7 @@
    * code.
    */
   if (aic7xxx_panic_on_abort)
-    aic7xxx_panic_abort(p);
+    aic7xxx_panic_abort(p, cmd);
 
   DRIVER_LOCK
 
@@ -8814,7 +10436,7 @@
           p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
         else
           cmd_prev->host_scribble = cmd_next->host_scribble;
-        cmd_next->done(cmd_next);
+        cmd_next->scsi_done(cmd_next);
         unpause_sequencer(p, FALSE);
         DRIVER_UNLOCK
         return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful
@@ -8838,7 +10460,12 @@
  *     command held by the scb pointer and is a valid abort request.
  *   Now, we just have to figure out what to do from here.  Current plan is:
  *     if we have already been here on this command, escalate to a reset
- *     if scb is on waiting list or QINFIFO, send it back as aborted
+ *     if scb is on waiting list or QINFIFO, send it back as aborted, but
+ *       we also need to be aware of the possibility that we could be using
+ *       a faked negotiation command that is holding this command up,  if
+ *       so we need to take care of that command instead, which means we
+ *       would then treat this one like it was sitting around disconnected
+ *       instead.
  *     if scb is on WAITING_SCB list in sequencer, free scb and send back
  *     if scb is disconnected and not completed, abort with abort message
  *     if scb is currently running, then it may be causing the bus to hang
@@ -8918,18 +10545,55 @@
 
   if ((found == 0) && (scb->flags & SCB_WAITINGQ))
   {
-      int tindex = TARGET_INDEX(cmd);
-     
-      if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
-        printk(INFO_LEAD "SCB found on waiting list and "
-            "aborted.\n", p->host_no, CTL_OF_SCB(scb));
-      scbq_remove(&p->waiting_scbs, scb);
-      scbq_remove(&p->delayed_scbs[tindex], scb);
-      p->dev_active_cmds[tindex]++;
-      p->activescbs++;
-      scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
-      scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+    int tindex = TARGET_INDEX(cmd);
+#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
+    unsigned short mask;
+
+    mask = (1 << tindex);
+
+    if (p->wdtr_pending & mask)
+    {
+      if (p->dev_wdtr_cmnd[tindex]->next != cmd)
+        found = 1;
+      else
+        found = 0;
+    }
+    else if (p->sdtr_pending & mask)
+    {
+      if (p->dev_sdtr_cmnd[tindex]->next != cmd)
+        found = 1;
+      else
+        found = 0;
+    }
+    else
+    {
       found = 1;
+    }
+    if (found == 0)
+    {
+      /*
+       * OK..this means the command we are currently getting an abort
+       * for has an outstanding negotiation command in front of it.
+       * We don't really have a way to tie back into the negotiation
+       * commands, so we just send this back as pending, then it
+       * will get reset in 2 seconds.
+       */
+      unpause_sequencer(p, TRUE);
+      scb->flags |= SCB_ABORT;
+      DRIVER_UNLOCK
+      return(SCSI_ABORT_PENDING);
+    }
+#endif
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
+      printk(INFO_LEAD "SCB found on waiting list and "
+          "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+    scbq_remove(&p->waiting_scbs, scb);
+    scbq_remove(&p->delayed_scbs[tindex], scb);
+    p->dev_active_cmds[tindex]++;
+    p->activescbs++;
+    scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+    scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+    found = 1;
   }
 
 /*
@@ -9022,7 +10686,10 @@
       printk(INFO_LEAD "SCB disconnected.  Queueing Abort"
         " SCB.\n", p->host_no, CTL_OF_SCB(scb));
     p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
   }
   if (found)
   {
@@ -9099,7 +10766,7 @@
    * code.
    */
   if (aic7xxx_panic_on_abort)
-    aic7xxx_panic_abort(p);
+    aic7xxx_panic_abort(p, cmd);
 
   DRIVER_LOCK
 
@@ -9219,7 +10886,7 @@
         "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
     action = BUS_RESET;
   }
-  if ( (action & BUS_RESET) && !(p->type & AHC_TWIN) )
+  if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) )
   {
     action = HOST_RESET;
   }
@@ -9286,16 +10953,11 @@
       p->reset_start = jiffies;
       p->flags |= AHC_IN_RESET;
       aic7xxx_reset_channel(p, cmd->channel, TRUE);
-      if ( (p->type & AHC_TWIN) && (action & HOST_RESET) )
+      if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
       {
         aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
         restart_sequencer(p);
       }
-      if (scb == NULL)
-      {
-        cmd->result = DID_RESET << 16;
-        cmd->done(cmd);
-      }
       p->last_reset = jiffies;
       if (action != HOST_RESET)
         result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
@@ -9377,6 +11039,7 @@
   if(p->irq)
     free_irq(p->irq, p);
   release_region(p->base, MAXREG - MINREG);
+#ifdef MMAPIO
   if(p->maddr)
   {
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
@@ -9385,6 +11048,7 @@
     iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK));
 #endif
   }
+#endif /* MMAPIO */
   prev = NULL;
   next = first_aic7xxx;
   while(next != NULL)
@@ -9405,6 +11069,207 @@
   aic7xxx_free(p);
   return(0);
 }
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_card
+ *
+ * Description:
+ *   Print out all of the control registers on the card
+ *
+ *   NOTE: This function is not yet safe for use on the VLB and EISA
+ *   controllers, so it isn't used on those controllers at all.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_card(struct aic7xxx_host *p)
+{
+  int i, j, k, chip;
+  static struct register_ranges {
+    int num_ranges;
+    int range_val[32];
+  } cards_ds[] = {
+    { 0, {0,} }, /* none */
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} },
+    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/
+          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/
+          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+          0xfe, 0xff} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a,
+          0x9f, 0x9f, 0xe0, 0xf1} },
+    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+          0xfe, 0xff} },
+  };
+#ifdef CONFIG_PCI
+  static struct register_ranges cards_ns[] = {
+    { 0, {0,} }, /* none */
+    { 0, {0,} }, /* 7771 */
+    { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33,
+          0x3c, 0x41, 0x43, 0x47} },
+    { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33,
+          0x3c, 0x41, 0x43, 0x47} },
+    { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x33, 0x3c, 0x41} },
+    { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47} },
+    { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} },
+    { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47,
+          0xdc, 0xe3} },
+    { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3,
+          0xff, 0xff} }
+  };
+#endif
+  chip = p->chip & AHC_CHIPID_MASK;
+  /*
+   * Let's run through the PCI space first....
+   */
+  printk("%s at ",
+         board_names[p->board_name_index]);
+  switch(p->chip & ~AHC_CHIPID_MASK)
+  {
+    case AHC_VL:
+      printk("VLB Slot %d.\n", p->pci_device_fn);
+      break;
+    case AHC_EISA:
+      printk("EISA Slot %d.\n", p->pci_device_fn);
+      break;
+    case AHC_PCI:
+    default:
+      printk("PCI %d/%d.\n", PCI_SLOT(p->pci_device_fn),
+             PCI_FUNC(p->pci_device_fn));
+      break;
+  }
+
+#ifdef CONFIG_PCI
+  {
+    unsigned char temp;
+    
+    printk("PCI Dump:\n");
+    k=0;
+    for(i=0; i<cards_ns[chip].num_ranges; i++)
+    {
+      for(j  = cards_ns[chip].range_val[ i * 2 ];
+          j <= cards_ns[chip].range_val[ i * 2 + 1 ] ;
+          j++)
+      {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+        pci_read_config_byte(p->pdev, j, &temp);
+#else
+        pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, j, &temp);
+#endif
+        printk("%02x:%02x ", j, temp);
+        if(++k == 13)
+        {
+          printk("\n");
+          k = 0;
+        }
+      }
+    }
+  }
+  if(k != 0)
+    printk("\n");
+#endif /* CONFIG_PCI */
+
+  /*
+   * Now the registers on the card....
+   */
+  printk("Card Dump:\n");
+  k = 0;
+  for(i=0; i<cards_ds[chip].num_ranges; i++)
+  {
+    for(j  = cards_ds[chip].range_val[ i * 2 ];
+        j <= cards_ds[chip].range_val[ i * 2 + 1 ] ;
+        j++)
+    {
+      printk("%02x:%02x ", j, aic_inb(p, j));
+      if(++k == 13)
+      {
+        printk("\n");
+        k=0;
+      }
+    }
+  }
+  if(k != 0)
+    printk("\n");
+  if (p->flags & AHC_SEEPROM_FOUND)
+  {
+    unsigned short *sc1;
+    sc1 = (unsigned short *)&p->sc;
+    
+    printk("SEEPROM dump.\n");
+    for(i=1; i<=32; i++)
+    {
+      printk("0x%04x", sc1[i-1]);
+      if ( (i % 8) == 0 )
+        printk("\n");
+      else
+        printk("  ");
+    }
+  }
+
+  /*
+   * If this was an Ultra2 controller, then we just hosed the card in terms
+   * of the QUEUE REGS.  This function is only called at init time or by
+   * the panic_abort function, so it's safe to assume a generic init time
+   * setting here
+   */
+
+  if(p->features & AHC_QUEUE_REGS)
+  {
+    aic_outb(p, 0, SDSCB_QOFF);
+    aic_outb(p, 0, SNSCB_QOFF);
+    aic_outb(p, 0, HNSCB_QOFF);
+  }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_scratch_ram
+ *
+ * Description:
+ *   Print out the scratch RAM values on the card.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
+{
+  int i, k;
+
+  k = 0;
+  printk("Scratch RAM:\n");
+  for(i = SRAM_BASE; i < SEQCTL; i++)
+  {
+    printk("%02x:%02x ", i, aic_inb(p, i));
+    if(++k == 13)
+    {
+      printk("\n");
+      k=0;
+    }
+  }
+  if (p->features & AHC_MORE_SRAM)
+  {
+    for(i = TARG_OFFSET; i < 0x80; i++)
+    {
+      printk("%02x:%02x ", i, aic_inb(p, i));
+      if(++k == 13)
+      {
+        printk("\n");
+        k=0;
+      }
+    }
+  }
+  printk("\n");
+}
+
 
 #include "aic7xxx_proc.c"
 
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.0.35/linux/drivers/scsi/aic7xxx_proc.c	Sun Nov 15 10:49:44 1998
+++ linux/drivers/scsi/aic7xxx_proc.c	Sun Nov 15 10:33:08 1998
@@ -85,10 +85,8 @@
   struct aic7xxx_host *p;
   int    size = 0;
   unsigned char i;
-#ifdef AIC7XXX_PROC_STATS
   struct aic7xxx_xferstats *sp;
   unsigned char target, lun;
-#endif
 
   HBAptr = NULL;
 
@@ -130,16 +128,18 @@
    */
 
   size = 4096;
-#ifdef AIC7XXX_PROC_STATS
   for (target = 0; target < MAX_TARGETS; target++)
   {
     for (lun = 0; lun < MAX_LUNS; lun++)
     {
       if (p->stats[target][lun].xfers != 0)
+#ifdef AIC7XXX_PROC_STATS
         size += 512;
+#else
+        size += 256;
+#endif
     }
   }
-#endif
   if (aic7xxx_buffer_size != size)
   {
     if (aic7xxx_buffer != NULL) 
@@ -195,9 +195,11 @@
       if (p->flags & (AHC_CHNLB|AHC_CHNLC))
         channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
     }
-    if (p->type & AHC_WIDE)
+    if (p->features & AHC_WIDE)
       wide = "Wide ";
-    if (p->type & AHC_ULTRA)
+    if (p->features & AHC_ULTRA2)
+      ultra = "Ultra2-LVD/SE ";
+    else if (p->features & AHC_ULTRA)
       ultra = "Ultra ";
     size += sprintf(BLS, "                           %s%sController%s\n",
       ultra, wide, channel);
@@ -210,25 +212,26 @@
   {
     size += sprintf(BLS, "    PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
   }
-  if( !(p->type & AHC_AIC78x0) )
+  if( (p->chip & (AHC_VL | AHC_EISA)) )
   {
     size += sprintf(BLS, "    BIOS Memory Address: 0x%08x\n", p->bios_address);
-    size += sprintf(BLS, "                         %s\n",
-            (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
-  }
-  else
-  {
-    size += sprintf(BLS, "      Adaptec SCSI BIOS: %s\n",
-            (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
   }
+  size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
+          (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
+         ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
+           "SEEPROM not found, using leftover BIOS values.") );
+  size += sprintf(BLS, "      Adaptec SCSI BIOS: %s\n",
+          (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
   size += sprintf(BLS, "                    IRQ: %d\n", HBAptr->irq);
   size += sprintf(BLS, "                   SCBs: Active %d, Max Active %d,\n",
             p->activescbs, p->max_activescbs);
   size += sprintf(BLS, "                         Allocated %d, HW %d, "
             "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
             p->scb_data->maxscbs);
+  if (p->flags & AHC_EXTERNAL_SRAM)
+    size += sprintf(BLS, "                         Using External SCB SRAM\n");
   size += sprintf(BLS, "             Interrupts: %ld", p->isr_count);
-  if (p->type & AHC_AIC7770)
+  if (p->chip & AHC_EISA)
   {
     size += sprintf(BLS, " %s\n",
         (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
@@ -244,7 +247,7 @@
   size += sprintf(BLS, "   Extended Translation: %sabled\n",
       (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
   size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
-  if (p->type & AHC_ULTRA)
+  if (p->features & (AHC_ULTRA | AHC_ULTRA2))
   {
     size += sprintf(BLS, "     Ultra Enable Flags: 0x%04x\n", p->ultraenb);
   }
@@ -268,7 +271,6 @@
     size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]);
   size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
 
-#ifdef AIC7XXX_PROC_STATS
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Statistics:\n");
   for (target = 0; target < MAX_TARGETS; target++)
@@ -280,7 +282,7 @@
       {
         continue;
       }
-      if (p->type & AHC_TWIN)
+      if (p->features & AHC_TWIN)
       {
         size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
             p->host_no, (target >> 3), (target & 0x7), lun);
@@ -290,10 +292,50 @@
         size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
             p->host_no, 0, target, lun);
       }
-      size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n",
+      size += sprintf(BLS, "  Device using %s/%s\n",
+            (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
+            "Wide" : "Narrow",
+            (p->transinfo[target].cur_offset != 0) ?
+            "Sync transfers at" : "Async transfers." );
+      if (p->transinfo[target].cur_offset != 0)
+      {
+        struct aic7xxx_syncrate *sync_rate;
+        int period = p->transinfo[target].cur_period;
+        int rate = (p->transinfo[target].cur_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+        sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+        if (sync_rate != NULL)
+        {
+          size += sprintf(BLS, "  %s MByte/sec, offset %d\n",
+                          sync_rate->rate[rate],
+                          p->transinfo[target].cur_offset );
+        }
+        else
+        {
+          size += sprintf(BLS, "  3.3 MByte/sec, offset %d\n",
+                          p->transinfo[target].cur_offset );
+        }
+      }
+      size += sprintf(BLS, "    Device Negotiation Settings\n");
+      size += sprintf(BLS, "        Period Offset Bus Width\n");
+      size += sprintf(BLS, "User       %03d    %03d        %d\n",
+                      p->transinfo[target].user_period,
+                      p->transinfo[target].user_offset,
+                      p->transinfo[target].user_width);
+      size += sprintf(BLS, "Goal       %03d    %03d        %d\n",
+                      p->transinfo[target].goal_period,
+                      p->transinfo[target].goal_offset,
+                      p->transinfo[target].goal_width);
+      size += sprintf(BLS, "Current    %03d    %03d        %d\n",
+                      p->transinfo[target].cur_period,
+                      p->transinfo[target].cur_offset,
+                      p->transinfo[target].cur_width);
+      size += sprintf(BLS, "    Total transfers %ld (%ld read;%ld written)\n",
           sp->xfers, sp->r_total, sp->w_total);
-      size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n",
+      size += sprintf(BLS, "      blks(512) rd=%ld; blks(512) wr=%ld\n",
           sp->r_total512, sp->w_total512);
+#ifdef AIC7XXX_PROC_STATS
       size += sprintf(BLS, "%s\n", HDRB);
       size += sprintf(BLS, " Reads:");
       for (i = 0; i < NUMBER(sp->r_bins); i++)
@@ -306,10 +348,10 @@
       {
         size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
       }
+#endif /* AIC7XXX_PROC_STATS */
       size += sprintf(BLS, "\n\n");
     }
   }
-#endif /* AIC7XXX_PROC_STATS */
 
   if (size >= aic7xxx_buffer_size)
   {
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- v2.0.35/linux/drivers/scsi/aic7xxx_reg.h	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/aic7xxx_reg.h	Sun Nov 15 10:33:08 1998
@@ -49,11 +49,13 @@
 
 #define	SCSIRATE        		0x04
 #define		WIDEXFER        	0x80
+#define		SXFR_ULTRA2     	0x7f
 #define		SXFR            	0x70
 #define		SOFS            	0x0f
 
 #define	SCSIID          		0x05
-#define		OID             	0x0f
+#define	SCSIOFFSET      		0x05
+#define		SOFS_ULTRA2     	0x7f
 
 #define	SCSIDATL        		0x06
 
@@ -73,6 +75,7 @@
 #define		SELDO           	0x40
 #define		SELDI           	0x20
 #define		SELINGO         	0x10
+#define		IOERR           	0x08
 #define		SWRAP           	0x08
 #define		SDONE           	0x04
 #define		SPIORDY         	0x02
@@ -100,20 +103,20 @@
 #define	SSTAT2          		0x0d
 #define		OVERRUN         	0x80
 #define		SFCNT           	0x1f
+#define		EXP_ACTIVE      	0x10
 
 #define	SSTAT3          		0x0e
 #define		SCSICNT         	0xf0
 #define		OFFCNT          	0x0f
 
-#define	SCSITEST        		0x0f
-#define		RQAKCNT         	0x04
-#define		CNTRTEST        	0x02
-#define		CMODE           	0x01
+#define	SCSIID_ULTRA2   		0x0f
+#define		OID             	0x0f
 
 #define	SIMODE0         		0x10
 #define		ENSELDO         	0x40
 #define		ENSELDI         	0x20
 #define		ENSELINGO       	0x10
+#define		ENIOERR         	0x08
 #define		ENSWRAP         	0x08
 #define		ENSDONE         	0x04
 #define		ENSPIORDY       	0x02
@@ -161,10 +164,15 @@
 #define		BRDDAT7         	0x80
 #define		BRDDAT6         	0x40
 #define		BRDDAT5         	0x20
+#define		BRDDAT4         	0x10
 #define		BRDSTB          	0x10
 #define		BRDCS           	0x08
+#define		BRDDAT3         	0x08
+#define		BRDDAT2         	0x04
 #define		BRDRW           	0x04
+#define		BRDRW_ULTRA2    	0x02
 #define		BRDCTL1         	0x02
+#define		BRDSTB_ULTRA2   	0x01
 #define		BRDCTL0         	0x01
 
 #define	SEECTL          		0x1e
@@ -181,11 +189,14 @@
 #define		DIAGLEDEN       	0x80
 #define		DIAGLEDON       	0x40
 #define		AUTOFLUSHDIS    	0x20
+#define		ENAB40          	0x08
+#define		ENAB20          	0x04
 #define		SELWIDE         	0x02
+#define		XCVR            	0x01
 
 #define	SRAM_BASE       		0x20
 
-#define	TARG_SCRATCH    		0x20
+#define	TARG_SCSIRATE   		0x20
 
 #define	ULTRA_ENB       		0x30
 
@@ -194,6 +205,7 @@
 #define	MSG_OUT         		0x34
 
 #define	DMAPARAMS       		0x35
+#define		PRELOADEN       	0x80
 #define		WIDEODD         	0x40
 #define		SCSIEN          	0x20
 #define		SDMAENACK       	0x10
@@ -258,7 +270,12 @@
 #define		SEND_REJ        	0x20
 #define		MSGOUT_PHASEMIS 	0x10
 
-#define	LAST_MSG        		0x52
+#define	ARG_2           		0x52
+#define	RETURN_2        		0x52
+
+#define	LAST_MSG        		0x53
+
+#define	PREFETCH_CNT    		0x54
 
 #define	SCSICONF        		0x5a
 #define		TERM_ENB        	0x80
@@ -314,10 +331,18 @@
 
 #define	STACK           		0x6f
 
+#define	TARG_OFFSET     		0x70
+
 #define	BCTL            		0x84
 #define		ACE             	0x08
 #define		ENABLE          	0x01
 
+#define	DSCOMMAND0      		0x84
+#define		INTSCBRAMSEL    	0x08
+#define		RAMPS           	0x04
+#define		USCBSIZE32      	0x02
+#define		CIOPARCKEN      	0x01
+
 #define	DSCOMMAND       		0x84
 #define		CACHETHEN       	0x80
 #define		DPARCKEN        	0x40
@@ -329,12 +354,12 @@
 #define		BON             	0x0f
 
 #define	BUSSPD          		0x86
-#define		DFTHRSH_100     	0xc0
 #define		DFTHRSH         	0xc0
 #define		STBOFF          	0x38
 #define		STBON           	0x07
 
 #define	DSPCISTATUS     		0x86
+#define		DFTHRSH_100     	0xc0
 
 #define	HCNTRL          		0x87
 #define		POWRDN          	0x40
@@ -381,6 +406,7 @@
 #define		CLRSEQINT       	0x01
 
 #define	ERROR           		0x92
+#define		CIOPARERR       	0x80
 #define		PCIERRSTAT      	0x40
 #define		MPARERR         	0x20
 #define		DPARERR         	0x10
@@ -392,6 +418,7 @@
 #define	DFCNTRL         		0x93
 
 #define	DFSTATUS        		0x94
+#define		PRELOAD_AVAIL   	0x80
 #define		DWORDEMP        	0x20
 #define		MREQPEND        	0x10
 #define		HDONE           	0x08
@@ -413,6 +440,8 @@
 
 #define	QOUTCNT         		0x9e
 
+#define	SFUNCT          		0x9f
+
 #define	SCB_CONTROL     		0xa0
 #define		MK_MESSAGE      	0x80
 #define		DISCENB         	0x40
@@ -464,25 +493,95 @@
 #define		ADSEL           	0x1e
 #define		DI_2840         	0x01
 
+#define	CCHADDR         		0xe0
+
+#define	CCHCNT          		0xe8
+
+#define	CCSGRAM         		0xe9
+
+#define	CCSGADDR        		0xea
+
+#define	CCSGCTL         		0xeb
+#define		CCSGDONE        	0x80
+#define		CCSGEN          	0x08
+#define		FLAG            	0x02
+#define		CCSGRESET       	0x01
+
+#define	CCSCBRAM        		0xec
+
+#define	CCSCBADDR       		0xed
+
+#define	CCSCBCTL        		0xee
+#define		CCSCBDONE       	0x80
+#define		ARRDONE         	0x40
+#define		CCARREN         	0x10
+#define		CCSCBEN         	0x08
+#define		CCSCBDIR        	0x04
+#define		CCSCBRESET      	0x01
+
+#define	CCSCBCNT        		0xef
+
+#define	CCSCBPTR        		0xf1
+
+#define	HNSCB_QOFF      		0xf4
+
+#define	SNSCB_QOFF      		0xf6
+
+#define	SDSCB_QOFF      		0xf8
+
+#define	QOFF_CTLSTA     		0xfa
+#define		SCB_AVAIL       	0x40
+#define		SNSCB_ROLLOVER  	0x20
+#define		SDSCB_ROLLOVER  	0x10
+#define		SCB_QSIZE       	0x07
+#define		SCB_QSIZE_256   	0x06
+
+#define	DFF_THRSH       		0xfb
+#define		WR_DFTHRSH      	0x70
+#define		WR_DFTHRSH_MAX  	0x70
+#define		WR_DFTHRSH_90   	0x60
+#define		WR_DFTHRSH_85   	0x50
+#define		WR_DFTHRSH_75   	0x40
+#define		WR_DFTHRSH_63   	0x30
+#define		WR_DFTHRSH_50   	0x20
+#define		WR_DFTHRSH_25   	0x10
+#define		RD_DFTHRSH_MAX  	0x07
+#define		RD_DFTHRSH      	0x07
+#define		RD_DFTHRSH_90   	0x06
+#define		RD_DFTHRSH_85   	0x05
+#define		RD_DFTHRSH_75   	0x04
+#define		RD_DFTHRSH_63   	0x03
+#define		RD_DFTHRSH_50   	0x02
+#define		RD_DFTHRSH_25   	0x01
+#define		WR_DFTHRSH_MIN  	0x00
+#define		RD_DFTHRSH_MIN  	0x00
+
+#define	SG_CACHEPTR     		0xfc
+#define		SG_USER_DATA    	0xfc
+#define		LAST_SEG        	0x02
+#define		LAST_SEG_DONE   	0x01
+
 
-#define	CMD_GROUP_CODE_SHIFT	0x05
-#define	BUS_8_BIT	0x00
-#define	QOUTFIFO_OFFSET	0x01
 #define	CMD_GROUP2_BYTE_DELTA	0xfa
 #define	MAX_OFFSET_8BIT	0x0f
 #define	BUS_16_BIT	0x01
 #define	QINFIFO_OFFSET	0x02
 #define	CMD_GROUP5_BYTE_DELTA	0x0b
+#define	CMD_GROUP_CODE_SHIFT	0x05
+#define	MAX_OFFSET_ULTRA2	0x7f
 #define	MAX_OFFSET_16BIT	0x08
+#define	BUS_8_BIT	0x00
+#define	QOUTFIFO_OFFSET	0x01
 #define	UNTAGGEDSCB_OFFSET	0x00
+#define	CCSGRAM_MAXSEGS	0x10
 #define	SCB_LIST_NULL	0xff
 #define	SG_SIZEOF	0x08
 #define	CMD_GROUP4_BYTE_DELTA	0x04
 #define	CMD_GROUP0_BYTE_DELTA	0xfc
 #define	HOST_MSG	0xff
 #define	BUS_32_BIT	0x02
+#define	CCSGADDR_MAX	0x80
 
 
 /* Downloaded Constant Definitions */
-#define	TMODE_NUMCMDS	0x01
-#define	QCNTMASK	0x00
+#define	TMODE_NUMCMDS	0x00
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_seq.c
--- v2.0.35/linux/drivers/scsi/aic7xxx_seq.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_seq.c	Sun Nov 15 10:33:08 1998
@@ -0,0 +1,769 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+static unsigned char seqprog[] = {
+	0xff, 0x6a, 0x06, 0x08,
+	0x32, 0x6a, 0x00, 0x00,
+	0x12, 0x6a, 0x00, 0x00,
+	0xff, 0x6a, 0xd6, 0x09,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x00, 0x65, 0x38, 0x59,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x4e, 0xc8, 0x08,
+	0xbf, 0x60, 0xc0, 0x08,
+	0x60, 0x0b, 0x7c, 0x68,
+	0x40, 0x00, 0x0e, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0x60, 0x0b, 0x7c, 0x68,
+	0x40, 0x00, 0x0e, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0xff, 0x3e, 0x3e, 0x60,
+	0x40, 0xfa, 0x10, 0x78,
+	0xff, 0xf6, 0xd4, 0x08,
+	0x01, 0x4e, 0x9c, 0x18,
+	0x40, 0x60, 0xc0, 0x00,
+	0x00, 0x4d, 0x10, 0x70,
+	0x01, 0x4e, 0x9c, 0x18,
+	0xbf, 0x60, 0xc0, 0x08,
+	0x00, 0x6a, 0x72, 0x5c,
+	0xff, 0x4e, 0xc8, 0x18,
+	0x02, 0x6a, 0x88, 0x5b,
+	0xff, 0x52, 0x20, 0x09,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0x52, 0xfe, 0x5b,
+	0xff, 0x3e, 0x74, 0x09,
+	0xff, 0x90, 0x7c, 0x08,
+	0xff, 0x3e, 0x20, 0x09,
+	0x00, 0x65, 0x44, 0x58,
+	0x00, 0x65, 0x0e, 0x40,
+	0xf7, 0x1f, 0xca, 0x08,
+	0x08, 0xa1, 0xc8, 0x08,
+	0x00, 0x65, 0xca, 0x00,
+	0xff, 0x65, 0x3e, 0x08,
+	0xf0, 0xa1, 0xc8, 0x08,
+	0x0f, 0x0f, 0x1e, 0x08,
+	0x00, 0x0f, 0x1e, 0x00,
+	0xf0, 0xa1, 0xc8, 0x08,
+	0x0f, 0x05, 0x0a, 0x08,
+	0x00, 0x05, 0x0a, 0x00,
+	0x5a, 0x6a, 0x00, 0x04,
+	0x12, 0x65, 0xc8, 0x00,
+	0x00, 0x01, 0x02, 0x00,
+	0x31, 0x6a, 0xca, 0x00,
+	0x80, 0x37, 0x64, 0x68,
+	0xff, 0x65, 0xca, 0x18,
+	0xff, 0x37, 0xdc, 0x08,
+	0xff, 0x6e, 0xc8, 0x08,
+	0x00, 0x6c, 0x6c, 0x78,
+	0x20, 0x01, 0x02, 0x00,
+	0x4c, 0x37, 0xc8, 0x28,
+	0x08, 0x1f, 0x74, 0x78,
+	0x08, 0x37, 0x6e, 0x00,
+	0x08, 0x64, 0xc8, 0x00,
+	0x70, 0x64, 0xca, 0x18,
+	0xff, 0x6c, 0x0a, 0x08,
+	0x20, 0x64, 0xca, 0x18,
+	0xff, 0x6c, 0x08, 0x0c,
+	0x40, 0x0b, 0x04, 0x69,
+	0x80, 0x0b, 0xf6, 0x78,
+	0xa4, 0x6a, 0x06, 0x00,
+	0x40, 0x6a, 0x16, 0x00,
+	0x10, 0x03, 0xf2, 0x78,
+	0xff, 0x50, 0xc8, 0x08,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x49, 0x6a, 0xee, 0x5b,
+	0x01, 0x6a, 0x26, 0x01,
+	0xff, 0x6a, 0xca, 0x08,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0x92, 0x78,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x06, 0xcc, 0x08,
+	0xff, 0x66, 0x32, 0x09,
+	0x01, 0x65, 0xca, 0x18,
+	0x80, 0x66, 0xa0, 0x78,
+	0xff, 0x66, 0xa2, 0x08,
+	0x10, 0x03, 0x90, 0x68,
+	0xfc, 0x65, 0xc8, 0x18,
+	0x00, 0x65, 0xa8, 0x48,
+	0xff, 0x6a, 0x32, 0x01,
+	0x01, 0x64, 0x18, 0x19,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x84, 0x6a, 0x06, 0x00,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0xb2, 0x78,
+	0xff, 0x06, 0xc8, 0x08,
+	0xff, 0x64, 0x32, 0x09,
+	0xff, 0x6a, 0xca, 0x08,
+	0x5b, 0x64, 0xc8, 0x28,
+	0x00, 0x62, 0xc4, 0x18,
+	0xfc, 0x65, 0xca, 0x18,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xfa, 0x65, 0xca, 0x18,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x04, 0x65, 0xca, 0x18,
+	0x0b, 0x65, 0xca, 0x18,
+	0xff, 0x65, 0xc8, 0x08,
+	0x00, 0x8c, 0x18, 0x19,
+	0x02, 0x0b, 0xce, 0x78,
+	0x01, 0x65, 0xd4, 0x60,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x06, 0x32, 0x09,
+	0xff, 0x65, 0xca, 0x18,
+	0xff, 0x65, 0xce, 0x68,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0x40, 0x51, 0xe6, 0x78,
+	0xe4, 0x6a, 0x06, 0x00,
+	0x08, 0x01, 0x02, 0x00,
+	0x04, 0x6a, 0x18, 0x5b,
+	0x01, 0x50, 0xa0, 0x18,
+	0x00, 0x50, 0xec, 0xe0,
+	0xff, 0x6a, 0xa0, 0x08,
+	0xff, 0x6a, 0x3a, 0x01,
+	0x02, 0x6a, 0x22, 0x01,
+	0x40, 0x51, 0xf2, 0x68,
+	0xff, 0x6a, 0x06, 0x08,
+	0x00, 0x65, 0x0e, 0x40,
+	0x20, 0x6a, 0x16, 0x00,
+	0xf0, 0x19, 0x6e, 0x08,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x08, 0x6a, 0x5a, 0x58,
+	0x08, 0x6a, 0x68, 0x00,
+	0x00, 0x65, 0x18, 0x41,
+	0x12, 0x6a, 0x00, 0x00,
+	0x40, 0x6a, 0x16, 0x00,
+	0xff, 0x3e, 0x20, 0x09,
+	0xff, 0xba, 0x7c, 0x08,
+	0xff, 0xa1, 0x6e, 0x08,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x08, 0x6a, 0x5a, 0x58,
+	0x80, 0x6a, 0x68, 0x00,
+	0x80, 0x36, 0x6c, 0x00,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xff, 0x3d, 0xc8, 0x08,
+	0xbf, 0x64, 0x48, 0x79,
+	0x80, 0x64, 0xf0, 0x71,
+	0xa0, 0x64, 0x0e, 0x72,
+	0xc0, 0x64, 0x08, 0x72,
+	0xe0, 0x64, 0x52, 0x72,
+	0x01, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0xf7, 0x11, 0x22, 0x08,
+	0x00, 0x65, 0x38, 0x59,
+	0xff, 0x06, 0xd4, 0x08,
+	0xf7, 0x01, 0x02, 0x08,
+	0x09, 0x0c, 0x32, 0x79,
+	0x08, 0x0c, 0x0e, 0x68,
+	0x01, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0x26, 0x09,
+	0xff, 0x6a, 0x08, 0x08,
+	0xdf, 0x01, 0x02, 0x08,
+	0x01, 0x6a, 0x7a, 0x00,
+	0x03, 0x36, 0x6c, 0x0c,
+	0x08, 0x6a, 0xcc, 0x00,
+	0xa9, 0x6a, 0xe8, 0x5b,
+	0x00, 0x65, 0x66, 0x41,
+	0xa8, 0x6a, 0x6a, 0x00,
+	0x79, 0x6a, 0x6a, 0x00,
+	0x40, 0x3d, 0x50, 0x69,
+	0x04, 0x35, 0x6a, 0x00,
+	0x00, 0x65, 0x3a, 0x5b,
+	0x80, 0x6a, 0xd4, 0x01,
+	0x10, 0x36, 0x42, 0x69,
+	0x10, 0x36, 0x6c, 0x00,
+	0x07, 0xac, 0x10, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xac, 0x6a, 0xe0, 0x5b,
+	0x00, 0x65, 0xda, 0x5b,
+	0xff, 0xa3, 0x70, 0x08,
+	0x39, 0x6a, 0xcc, 0x00,
+	0xa4, 0x6a, 0xe6, 0x5b,
+	0xff, 0x38, 0x74, 0x69,
+	0x80, 0x02, 0x04, 0x00,
+	0xe7, 0x35, 0x6a, 0x08,
+	0x03, 0x69, 0x18, 0x31,
+	0xff, 0x6a, 0x10, 0x00,
+	0xff, 0x6a, 0x12, 0x00,
+	0xff, 0x6a, 0x14, 0x00,
+	0x01, 0x38, 0x7a, 0x61,
+	0x02, 0xfc, 0xf8, 0x01,
+	0xbf, 0x35, 0x6a, 0x08,
+	0xff, 0x69, 0xca, 0x08,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x0b, 0x7e, 0x69,
+	0x04, 0x0b, 0x8a, 0x69,
+	0x10, 0x0c, 0x80, 0x79,
+	0x04, 0x0b, 0x88, 0x69,
+	0xff, 0x6a, 0xca, 0x08,
+	0x00, 0x35, 0x22, 0x5b,
+	0x80, 0x02, 0xd6, 0x69,
+	0xff, 0x65, 0xc8, 0x79,
+	0xff, 0x38, 0x70, 0x18,
+	0xff, 0x38, 0xc8, 0x79,
+	0x80, 0xea, 0xaa, 0x61,
+	0xef, 0x38, 0xc8, 0x18,
+	0x80, 0x6a, 0xc8, 0x00,
+	0x00, 0x65, 0x9c, 0x49,
+	0x33, 0x38, 0xc8, 0x28,
+	0xff, 0x64, 0xd0, 0x09,
+	0x04, 0x39, 0xc0, 0x31,
+	0x09, 0x6a, 0xd6, 0x01,
+	0x80, 0xeb, 0xa2, 0x79,
+	0xf7, 0xeb, 0xd6, 0x09,
+	0x08, 0xeb, 0xa6, 0x69,
+	0x01, 0x6a, 0xd6, 0x01,
+	0x08, 0xe9, 0x10, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x39, 0x6a, 0xe6, 0x5b,
+	0x08, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x0d, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0x88, 0x6a, 0x54, 0x5c,
+	0x00, 0x65, 0xda, 0x5b,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x08, 0x39, 0x72, 0x18,
+	0x00, 0x3a, 0x74, 0x20,
+	0x10, 0x0c, 0x66, 0x79,
+	0x80, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0xe0, 0x59,
+	0xff, 0x08, 0x52, 0x09,
+	0xff, 0x09, 0x54, 0x09,
+	0xff, 0x0a, 0x56, 0x09,
+	0xff, 0x38, 0x50, 0x09,
+	0x12, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0xe0, 0x59,
+	0x12, 0x01, 0x02, 0x00,
+	0x7f, 0x02, 0x04, 0x08,
+	0xe1, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0x04, 0x93, 0xea, 0x69,
+	0xdf, 0x93, 0x26, 0x09,
+	0x20, 0x93, 0xe4, 0x69,
+	0x02, 0x93, 0x26, 0x01,
+	0x01, 0x94, 0xe6, 0x79,
+	0xd7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0xec, 0x69,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x00, 0x65, 0x3a, 0x5b,
+	0x02, 0xfc, 0xf8, 0x01,
+	0x05, 0xb4, 0x10, 0x31,
+	0x02, 0x6a, 0x1a, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xb4, 0x6a, 0xe4, 0x5b,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x00, 0x65, 0xda, 0x5b,
+	0x3d, 0x6a, 0x22, 0x5b,
+	0xac, 0x6a, 0x22, 0x5b,
+	0x00, 0x65, 0x18, 0x41,
+	0x00, 0x65, 0x3a, 0x5b,
+	0xff, 0x06, 0x44, 0x09,
+	0x00, 0x65, 0x18, 0x41,
+	0xff, 0x34, 0xca, 0x08,
+	0x80, 0x65, 0x32, 0x62,
+	0x0f, 0xa1, 0xca, 0x08,
+	0x07, 0xa1, 0xca, 0x08,
+	0x40, 0xa0, 0xc8, 0x08,
+	0x00, 0x65, 0xca, 0x00,
+	0x80, 0x65, 0xca, 0x00,
+	0x80, 0xa0, 0x22, 0x7a,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0x34, 0x42,
+	0x20, 0xa0, 0x3a, 0x7a,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xa0, 0x3d, 0x46, 0x62,
+	0x23, 0xa0, 0x0c, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xa0, 0x3d, 0x46, 0x62,
+	0x00, 0xb9, 0x3a, 0x42,
+	0xff, 0x65, 0x3a, 0x62,
+	0xa1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x10, 0x51, 0x46, 0x72,
+	0x40, 0x6a, 0x18, 0x00,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xa0, 0x3d, 0x46, 0x62,
+	0x10, 0x3d, 0x06, 0x00,
+	0x00, 0x65, 0x0e, 0x42,
+	0x40, 0x6a, 0x18, 0x00,
+	0xff, 0x34, 0xa6, 0x08,
+	0x80, 0x34, 0x4e, 0x62,
+	0x7f, 0xa0, 0x40, 0x09,
+	0x08, 0x6a, 0x68, 0x00,
+	0x00, 0x65, 0x18, 0x41,
+	0x64, 0x6a, 0x12, 0x5b,
+	0x80, 0x64, 0xbe, 0x6a,
+	0x04, 0x64, 0xa4, 0x72,
+	0x02, 0x64, 0xaa, 0x72,
+	0x00, 0x6a, 0x6c, 0x72,
+	0x03, 0x64, 0xba, 0x72,
+	0x01, 0x64, 0xa0, 0x72,
+	0x07, 0x64, 0x00, 0x73,
+	0x08, 0x64, 0x68, 0x72,
+	0x11, 0x6a, 0x22, 0x01,
+	0x07, 0x6a, 0x04, 0x5b,
+	0xff, 0x06, 0xd4, 0x08,
+	0x00, 0x65, 0x18, 0x41,
+	0xff, 0xa8, 0x70, 0x6a,
+	0xff, 0xa2, 0x88, 0x7a,
+	0x01, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xfe, 0x5b,
+	0xff, 0xa2, 0x88, 0x7a,
+	0x71, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x40, 0x51, 0x88, 0x62,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xfe, 0x5b,
+	0xff, 0x3e, 0x74, 0x09,
+	0xff, 0x90, 0x7c, 0x08,
+	0x00, 0x65, 0x44, 0x58,
+	0x00, 0x65, 0x2a, 0x41,
+	0x20, 0xa0, 0x90, 0x6a,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0x6a, 0xa8, 0x5b,
+	0xff, 0x6a, 0xbe, 0x5b,
+	0xff, 0xf8, 0xc8, 0x08,
+	0xff, 0x4f, 0xc8, 0x08,
+	0x01, 0x6a, 0xa8, 0x5b,
+	0x00, 0xb9, 0xbe, 0x5b,
+	0x01, 0x4f, 0x9e, 0x18,
+	0x02, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x6c, 0x5c,
+	0x00, 0x65, 0x2a, 0x41,
+	0x41, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0x04, 0xa0, 0x40, 0x01,
+	0x00, 0x65, 0x84, 0x5c,
+	0x00, 0x65, 0x2a, 0x41,
+	0x10, 0x36, 0x68, 0x7a,
+	0xff, 0x38, 0x46, 0x09,
+	0xa4, 0x6a, 0xcc, 0x00,
+	0x39, 0x6a, 0xe6, 0x5b,
+	0xac, 0x6a, 0xcc, 0x00,
+	0x14, 0x6a, 0xe6, 0x5b,
+	0xa9, 0x6a, 0xe8, 0x5b,
+	0x00, 0x65, 0x68, 0x42,
+	0xef, 0x36, 0x6c, 0x08,
+	0x00, 0x65, 0x68, 0x42,
+	0x0f, 0x64, 0xc8, 0x08,
+	0x07, 0x64, 0xc8, 0x08,
+	0x00, 0x37, 0x6e, 0x00,
+	0x00, 0x65, 0x78, 0x5b,
+	0xff, 0x51, 0xce, 0x72,
+	0x20, 0x36, 0xde, 0x7a,
+	0x00, 0x90, 0x5c, 0x5b,
+	0x00, 0x65, 0xe0, 0x42,
+	0xff, 0x06, 0xd4, 0x08,
+	0x00, 0x65, 0xd2, 0x5b,
+	0xe0, 0x3d, 0xfa, 0x62,
+	0x20, 0x12, 0xfa, 0x62,
+	0x51, 0x6a, 0x08, 0x5b,
+	0xff, 0x51, 0x20, 0x09,
+	0x20, 0xa0, 0xfa, 0x7a,
+	0x00, 0x90, 0x5c, 0x5b,
+	0x00, 0x65, 0x56, 0x5b,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0xa1, 0xf2, 0x62,
+	0x04, 0xa0, 0xf2, 0x7a,
+	0xfb, 0xa0, 0x40, 0x09,
+	0x80, 0x36, 0x6c, 0x00,
+	0x80, 0xa0, 0x68, 0x7a,
+	0x7f, 0xa0, 0x40, 0x09,
+	0xff, 0x6a, 0x04, 0x5b,
+	0x00, 0x65, 0x68, 0x42,
+	0x04, 0xa0, 0xf8, 0x7a,
+	0x00, 0x65, 0x84, 0x5c,
+	0x00, 0x65, 0xfa, 0x42,
+	0x00, 0x65, 0x6c, 0x5c,
+	0x31, 0x6a, 0x22, 0x01,
+	0x0c, 0x6a, 0x04, 0x5b,
+	0x00, 0x65, 0x68, 0x42,
+	0x61, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x68, 0x42,
+	0x10, 0x3d, 0x06, 0x00,
+	0xff, 0x65, 0x68, 0x0c,
+	0xff, 0x06, 0xd4, 0x08,
+	0x01, 0x0c, 0x0a, 0x7b,
+	0x04, 0x0c, 0x0a, 0x6b,
+	0xe0, 0x03, 0x7a, 0x08,
+	0xe0, 0x3d, 0x1e, 0x63,
+	0xff, 0x65, 0xcc, 0x08,
+	0xff, 0x12, 0xda, 0x0c,
+	0xff, 0x06, 0xd4, 0x0c,
+	0xff, 0x65, 0x0c, 0x08,
+	0x02, 0x0b, 0x1a, 0x7b,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0xd1, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x18, 0x41,
+	0xff, 0x65, 0x26, 0x09,
+	0x01, 0x0b, 0x32, 0x6b,
+	0x10, 0x0c, 0x24, 0x7b,
+	0x04, 0x0b, 0x2c, 0x6b,
+	0xff, 0x6a, 0xca, 0x08,
+	0x04, 0x93, 0x30, 0x6b,
+	0x01, 0x94, 0x2e, 0x7b,
+	0x10, 0x94, 0x30, 0x6b,
+	0xc7, 0x93, 0x26, 0x09,
+	0xff, 0x99, 0xd4, 0x08,
+	0x08, 0x93, 0x34, 0x6b,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x80, 0x36, 0x38, 0x6b,
+	0x21, 0x6a, 0x22, 0x05,
+	0xff, 0x65, 0x20, 0x09,
+	0xff, 0x51, 0x46, 0x63,
+	0xff, 0x37, 0xc8, 0x08,
+	0xa1, 0x6a, 0x50, 0x43,
+	0xff, 0x51, 0xc8, 0x08,
+	0xb9, 0x6a, 0x50, 0x43,
+	0xff, 0xba, 0x54, 0x73,
+	0xff, 0xba, 0x20, 0x09,
+	0xff, 0x65, 0xca, 0x18,
+	0x00, 0x6c, 0x4a, 0x63,
+	0xff, 0x90, 0xca, 0x0c,
+	0xff, 0x6a, 0xca, 0x04,
+	0x20, 0x36, 0x72, 0x7b,
+	0x00, 0x90, 0x3e, 0x5b,
+	0xff, 0x65, 0x72, 0x73,
+	0xff, 0xba, 0x66, 0x73,
+	0xff, 0xbb, 0xcc, 0x08,
+	0xff, 0xba, 0x20, 0x09,
+	0xff, 0x66, 0x76, 0x09,
+	0xff, 0x65, 0x20, 0x09,
+	0xff, 0xbb, 0x70, 0x73,
+	0xff, 0xba, 0xcc, 0x08,
+	0xff, 0xbb, 0x20, 0x09,
+	0xff, 0x66, 0x74, 0x09,
+	0xff, 0x65, 0x20, 0x0d,
+	0xff, 0xba, 0x7e, 0x0c,
+	0x00, 0x6a, 0x72, 0x5c,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0x51, 0xfe, 0x43,
+	0xff, 0x3f, 0xcc, 0x73,
+	0xff, 0x6a, 0xa2, 0x00,
+	0x00, 0x3f, 0x3e, 0x5b,
+	0xff, 0x65, 0xcc, 0x73,
+	0x20, 0x36, 0x6c, 0x00,
+	0x20, 0xa0, 0x86, 0x6b,
+	0xff, 0xb9, 0xa2, 0x0c,
+	0xff, 0x6a, 0xa2, 0x04,
+	0xff, 0x65, 0xa4, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x09, 0x6a, 0xd6, 0x01,
+	0x80, 0xeb, 0x92, 0x7b,
+	0x01, 0x6a, 0xd6, 0x01,
+	0x01, 0xe9, 0xa4, 0x34,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x0d, 0x6a, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0xff, 0x99, 0xa4, 0x0c,
+	0xff, 0x65, 0xa4, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x01, 0x6a, 0xdc, 0x05,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xf2, 0x5b,
+	0x01, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x01, 0x6a, 0x26, 0x05,
+	0x01, 0x65, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0xc2, 0x7b,
+	0xff, 0x6a, 0xdc, 0x0d,
+	0xff, 0x65, 0x32, 0x09,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x44,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0x6a, 0x88, 0x5b,
+	0xff, 0x52, 0xa2, 0x0c,
+	0x01, 0x0c, 0xd2, 0x7b,
+	0x04, 0x0c, 0xd2, 0x6b,
+	0xe0, 0x03, 0x7a, 0x08,
+	0xff, 0x3d, 0x06, 0x0c,
+	0xff, 0x8c, 0x10, 0x08,
+	0xff, 0x8d, 0x12, 0x08,
+	0xff, 0x8e, 0x14, 0x0c,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x0c,
+	0x3d, 0x64, 0xa4, 0x28,
+	0x55, 0x64, 0xc8, 0x28,
+	0x00, 0x6c, 0xda, 0x18,
+	0xff, 0x52, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0x00, 0x6c, 0xda, 0x24,
+	0xff, 0x65, 0xc8, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x41, 0x6a, 0xee, 0x5b,
+	0xff, 0x90, 0xe2, 0x09,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x04, 0x35, 0x10, 0x7c,
+	0x1d, 0x6a, 0xdc, 0x01,
+	0xdc, 0xee, 0x0c, 0x64,
+	0x00, 0x65, 0x1c, 0x44,
+	0x01, 0x6a, 0xdc, 0x01,
+	0x20, 0xa0, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0x16, 0x7c,
+	0x19, 0x6a, 0xdc, 0x01,
+	0xd8, 0xee, 0x1a, 0x64,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x18, 0xee, 0x1e, 0x6c,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x41, 0x6a, 0xee, 0x5b,
+	0x20, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x35, 0x48, 0x6c,
+	0xa0, 0x6a, 0xca, 0x00,
+	0x20, 0x65, 0xc8, 0x18,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0x00, 0x65, 0x34, 0x64,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x64, 0x5c,
+	0x04, 0x35, 0x38, 0x7b,
+	0xa0, 0x6a, 0x54, 0x5c,
+	0x00, 0x65, 0x56, 0x5c,
+	0x00, 0x65, 0x56, 0x5c,
+	0x00, 0x65, 0x56, 0x44,
+	0xff, 0x65, 0xcc, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x0c,
+	0x08, 0x94, 0x64, 0x7c,
+	0xf7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0x68, 0x6c,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0xff, 0x40, 0x74, 0x09,
+	0xff, 0x90, 0x80, 0x08,
+	0xff, 0x6a, 0x72, 0x05,
+	0xff, 0x40, 0x80, 0x64,
+	0xff, 0x3f, 0x78, 0x64,
+	0xff, 0x6a, 0xca, 0x04,
+	0xff, 0x3f, 0x20, 0x09,
+	0x01, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xfe, 0x5b,
+	0x00, 0x90, 0x5c, 0x43,
+	0xff, 0x40, 0x20, 0x09,
+	0xff, 0xba, 0x80, 0x0c,
+	0xff, 0x6a, 0x76, 0x01,
+	0xff, 0x3f, 0x74, 0x09,
+	0xff, 0x90, 0x7e, 0x08,
+	0xff, 0xba, 0x38, 0x73,
+	0xff, 0xba, 0x20, 0x09,
+	0xff, 0x3f, 0x76, 0x09,
+	0xff, 0x3f, 0x20, 0x0d,
+};
+
+static int aic7xxx_patch12_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch12_func(struct aic7xxx_host *p)
+{
+	return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+}
+
+static int aic7xxx_patch11_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch11_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_WIDE) != 0);
+}
+
+static int aic7xxx_patch10_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch10_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA2) == 0);
+}
+
+static int aic7xxx_patch9_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch9_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA) != 0);
+}
+
+static int aic7xxx_patch8_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch8_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA2) != 0);
+}
+
+static int aic7xxx_patch7_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch7_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_PAGESCBS) == 0);
+}
+
+static int aic7xxx_patch6_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch6_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_PAGESCBS) != 0);
+}
+
+static int aic7xxx_patch5_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch5_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int aic7xxx_patch4_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch4_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_TWIN) != 0);
+}
+
+static int aic7xxx_patch3_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch3_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int aic7xxx_patch2_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch2_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_CMD_CHAN) != 0);
+}
+
+static int aic7xxx_patch1_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch1_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_TARGETMODE) != 0);
+}
+
+static int aic7xxx_patch0_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch0_func(struct aic7xxx_host *p)
+{
+	return (0);
+}
+
+struct sequencer_patch {
+	int		(*patch_func)(struct aic7xxx_host *);
+	unsigned int	begin	   :10,
+			skip_instr :10,
+			skip_patch :12;
+} sequencer_patches[] = {
+	{ aic7xxx_patch1_func, 1, 1, 2 },
+	{ aic7xxx_patch0_func, 2, 1, 1 },
+	{ aic7xxx_patch2_func, 3, 2, 1 },
+	{ aic7xxx_patch3_func, 7, 1, 1 },
+	{ aic7xxx_patch3_func, 8, 1, 1 },
+	{ aic7xxx_patch4_func, 11, 4, 1 },
+	{ aic7xxx_patch5_func, 16, 3, 2 },
+	{ aic7xxx_patch0_func, 19, 4, 1 },
+	{ aic7xxx_patch6_func, 23, 1, 1 },
+	{ aic7xxx_patch7_func, 26, 1, 1 },
+	{ aic7xxx_patch4_func, 34, 4, 1 },
+	{ aic7xxx_patch8_func, 38, 3, 2 },
+	{ aic7xxx_patch0_func, 41, 3, 1 },
+	{ aic7xxx_patch9_func, 47, 7, 1 },
+	{ aic7xxx_patch4_func, 55, 3, 1 },
+	{ aic7xxx_patch8_func, 58, 2, 1 },
+	{ aic7xxx_patch1_func, 63, 60, 1 },
+	{ aic7xxx_patch8_func, 164, 1, 2 },
+	{ aic7xxx_patch0_func, 165, 1, 1 },
+	{ aic7xxx_patch2_func, 169, 1, 1 },
+	{ aic7xxx_patch2_func, 172, 1, 2 },
+	{ aic7xxx_patch0_func, 173, 2, 1 },
+	{ aic7xxx_patch10_func, 175, 1, 1 },
+	{ aic7xxx_patch8_func, 182, 1, 2 },
+	{ aic7xxx_patch0_func, 183, 3, 1 },
+	{ aic7xxx_patch8_func, 187, 1, 2 },
+	{ aic7xxx_patch0_func, 188, 1, 1 },
+	{ aic7xxx_patch8_func, 189, 7, 2 },
+	{ aic7xxx_patch0_func, 196, 1, 1 },
+	{ aic7xxx_patch2_func, 201, 13, 2 },
+	{ aic7xxx_patch0_func, 214, 8, 1 },
+	{ aic7xxx_patch10_func, 222, 1, 1 },
+	{ aic7xxx_patch8_func, 227, 1, 1 },
+	{ aic7xxx_patch8_func, 228, 1, 1 },
+	{ aic7xxx_patch8_func, 233, 1, 1 },
+	{ aic7xxx_patch8_func, 235, 2, 1 },
+	{ aic7xxx_patch8_func, 240, 8, 1 },
+	{ aic7xxx_patch8_func, 249, 1, 1 },
+	{ aic7xxx_patch2_func, 250, 2, 2 },
+	{ aic7xxx_patch0_func, 252, 4, 1 },
+	{ aic7xxx_patch10_func, 256, 2, 2 },
+	{ aic7xxx_patch0_func, 258, 1, 1 },
+	{ aic7xxx_patch11_func, 265, 1, 2 },
+	{ aic7xxx_patch0_func, 266, 1, 1 },
+	{ aic7xxx_patch5_func, 328, 1, 2 },
+	{ aic7xxx_patch0_func, 329, 1, 1 },
+	{ aic7xxx_patch3_func, 332, 1, 1 },
+	{ aic7xxx_patch11_func, 351, 1, 2 },
+	{ aic7xxx_patch0_func, 352, 1, 1 },
+	{ aic7xxx_patch6_func, 356, 1, 1 },
+	{ aic7xxx_patch7_func, 364, 3, 2 },
+	{ aic7xxx_patch0_func, 367, 1, 1 },
+	{ aic7xxx_patch1_func, 396, 3, 1 },
+	{ aic7xxx_patch10_func, 410, 1, 1 },
+	{ aic7xxx_patch2_func, 453, 7, 2 },
+	{ aic7xxx_patch0_func, 460, 8, 1 },
+	{ aic7xxx_patch2_func, 469, 4, 2 },
+	{ aic7xxx_patch0_func, 473, 6, 1 },
+	{ aic7xxx_patch2_func, 479, 4, 2 },
+	{ aic7xxx_patch0_func, 483, 3, 1 },
+	{ aic7xxx_patch2_func, 512, 17, 4 },
+	{ aic7xxx_patch12_func, 520, 4, 2 },
+	{ aic7xxx_patch0_func, 524, 2, 1 },
+	{ aic7xxx_patch0_func, 529, 33, 1 },
+	{ aic7xxx_patch6_func, 566, 2, 1 },
+	{ aic7xxx_patch6_func, 569, 9, 1 },
+
+};
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h
--- v2.0.35/linux/drivers/scsi/aic7xxx_seq.h	Sun Nov 15 10:49:44 1998
+++ linux/drivers/scsi/aic7xxx_seq.h	Wed Dec 31 16:00:00 1969
@@ -1,524 +0,0 @@
-/*
-  * DO NOT EDIT - This file is automatically generated.
-  */
-static unsigned char seqprog[] = {
-	0xff, 0x6a, 0x03, 0x02,
-	0x32, 0x6a, 0x00, 0x00,
-	0x12, 0x6a, 0x00, 0x00,
-	0x00, 0x65, 0x92, 0x16,
-	0xf7, 0x01, 0x01, 0x02,
-	0xff, 0x4e, 0x64, 0x02,
-	0xbf, 0x60, 0x60, 0x02,
-	0x60, 0x0b, 0x37, 0x1a,
-	0x40, 0x00, 0x05, 0x1a,
-	0x08, 0x1f, 0x1f, 0x04,
-	0x60, 0x0b, 0x37, 0x1a,
-	0x40, 0x00, 0x05, 0x1a,
-	0x08, 0x1f, 0x1f, 0x04,
-	0xff, 0x3e, 0x1d, 0x18,
-	0x40, 0x60, 0x60, 0x00,
-	0x00, 0x4d, 0x06, 0x1c,
-	0x01, 0x4e, 0x4e, 0x06,
-	0xbf, 0x60, 0x60, 0x02,
-	0x00, 0x6a, 0xd8, 0x17,
-	0xff, 0x4e, 0x64, 0x06,
-	0x02, 0x6a, 0x93, 0x17,
-	0x0d, 0x6a, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0xff, 0x99, 0x65, 0x02,
-	0xff, 0x65, 0x90, 0x02,
-	0x0d, 0x6a, 0x35, 0x00,
-	0x00, 0x65, 0xb3, 0x17,
-	0xff, 0x3e, 0xba, 0x02,
-	0xff, 0x90, 0x3e, 0x02,
-	0xff, 0x3e, 0x90, 0x02,
-	0x00, 0x65, 0x20, 0x16,
-	0x00, 0x65, 0x05, 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,
-	0x12, 0x65, 0x64, 0x00,
-	0x00, 0x01, 0x01, 0x00,
-	0x31, 0x6a, 0x65, 0x00,
-	0x80, 0x37, 0x2d, 0x1a,
-	0xff, 0x65, 0x65, 0x06,
-	0xff, 0x37, 0x6e, 0x02,
-	0xff, 0x6e, 0x64, 0x02,
-	0x00, 0x6c, 0x31, 0x1e,
-	0x20, 0x01, 0x01, 0x00,
-	0x4c, 0x37, 0x64, 0x0a,
-	0x08, 0x1f, 0x35, 0x1e,
-	0x08, 0x37, 0x37, 0x00,
-	0x08, 0x64, 0x64, 0x00,
-	0x20, 0x64, 0x65, 0x06,
-	0xff, 0x6c, 0x04, 0x03,
-	0x40, 0x0b, 0x78, 0x1a,
-	0x80, 0x0b, 0x71, 0x1e,
-	0xa4, 0x6a, 0x03, 0x00,
-	0x40, 0x6a, 0x0b, 0x00,
-	0x10, 0x03, 0x6f, 0x1e,
-	0xff, 0x50, 0x64, 0x02,
-	0x49, 0x6a, 0xa9, 0x17,
-	0x01, 0x6a, 0x93, 0x00,
-	0xff, 0x6a, 0x65, 0x02,
-	0x08, 0x01, 0x01, 0x00,
-	0x02, 0x0b, 0x41, 0x1e,
-	0xf7, 0x01, 0x01, 0x02,
-	0xff, 0x06, 0x66, 0x02,
-	0xff, 0x66, 0x99, 0x02,
-	0x01, 0x65, 0x65, 0x06,
-	0x80, 0x66, 0x48, 0x1e,
-	0xff, 0x66, 0x51, 0x02,
-	0x10, 0x03, 0x40, 0x1a,
-	0xfc, 0x65, 0x64, 0x06,
-	0x00, 0x65, 0x4c, 0x12,
-	0xff, 0x6a, 0x99, 0x00,
-	0x01, 0x64, 0x8c, 0x06,
-	0x84, 0x6a, 0x03, 0x00,
-	0x08, 0x01, 0x01, 0x00,
-	0x02, 0x0b, 0x4f, 0x1e,
-	0xff, 0x06, 0x64, 0x02,
-	0xff, 0x64, 0x99, 0x02,
-	0xff, 0x6a, 0x65, 0x02,
-	0x5b, 0x64, 0x64, 0x0a,
-	0x00, 0x62, 0x62, 0x06,
-	0xfc, 0x65, 0x65, 0x06,
-	0xff, 0x6a, 0x6a, 0x02,
-	0xfa, 0x65, 0x65, 0x06,
-	0xff, 0x6a, 0x6a, 0x02,
-	0x04, 0x65, 0x65, 0x06,
-	0x0b, 0x65, 0x65, 0x06,
-	0xff, 0x65, 0x64, 0x02,
-	0x00, 0x8c, 0x8c, 0x06,
-	0x02, 0x0b, 0x5d, 0x1e,
-	0x01, 0x65, 0x60, 0x18,
-	0xf7, 0x01, 0x01, 0x02,
-	0xff, 0x06, 0x99, 0x02,
-	0xff, 0x65, 0x65, 0x06,
-	0xff, 0x65, 0x5d, 0x1a,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x40, 0x51, 0x69, 0x1e,
-	0xe4, 0x6a, 0x03, 0x00,
-	0x08, 0x01, 0x01, 0x00,
-	0x04, 0x6a, 0x5c, 0x17,
-	0x01, 0x50, 0x50, 0x06,
-	0x01, 0x50, 0x6c, 0x98,
-	0xff, 0x6a, 0x50, 0x02,
-	0xff, 0x6a, 0x9d, 0x00,
-	0x02, 0x6a, 0x91, 0x00,
-	0x40, 0x51, 0x6f, 0x1a,
-	0xff, 0x6a, 0x03, 0x02,
-	0x00, 0x65, 0x05, 0x10,
-	0x20, 0x6a, 0x0b, 0x00,
-	0xf0, 0x19, 0x37, 0x02,
-	0x08, 0x6a, 0x0c, 0x00,
-	0x08, 0x11, 0x11, 0x00,
-	0x08, 0x6a, 0x28, 0x16,
-	0x08, 0x6a, 0x34, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x12, 0x6a, 0x00, 0x00,
-	0x40, 0x6a, 0x0b, 0x00,
-	0xff, 0x3e, 0x90, 0x02,
-	0xff, 0xba, 0x3e, 0x02,
-	0xff, 0xa1, 0x37, 0x02,
-	0x08, 0x6a, 0x0c, 0x00,
-	0x08, 0x11, 0x11, 0x00,
-	0x08, 0x6a, 0x28, 0x16,
-	0x80, 0x6a, 0x34, 0x00,
-	0x80, 0x36, 0x36, 0x00,
-	0x00, 0x65, 0x9b, 0x17,
-	0xff, 0x3d, 0x64, 0x02,
-	0xbf, 0x64, 0x9a, 0x1e,
-	0x80, 0x64, 0xc9, 0x1c,
-	0xa0, 0x64, 0xd4, 0x1c,
-	0xc0, 0x64, 0xd1, 0x1c,
-	0xe0, 0x64, 0xf5, 0x1c,
-	0x01, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0xf7, 0x11, 0x11, 0x02,
-	0x00, 0x65, 0x92, 0x16,
-	0xff, 0x06, 0x6a, 0x02,
-	0xf7, 0x01, 0x01, 0x02,
-	0x09, 0x0c, 0x8f, 0x1e,
-	0x08, 0x0c, 0x05, 0x1a,
-	0x01, 0x6a, 0x91, 0x00,
-	0xff, 0x6a, 0x93, 0x02,
-	0xff, 0x6a, 0x04, 0x02,
-	0xdf, 0x01, 0x01, 0x02,
-	0x01, 0x6a, 0x3d, 0x00,
-	0x03, 0x36, 0x36, 0x03,
-	0x08, 0x6a, 0x66, 0x00,
-	0xa9, 0x6a, 0xa6, 0x17,
-	0x00, 0x65, 0xa5, 0x10,
-	0x79, 0x6a, 0x35, 0x00,
-	0x40, 0x3d, 0x9d, 0x1a,
-	0x04, 0x35, 0x35, 0x00,
-	0x00, 0x65, 0x6c, 0x17,
-	0x10, 0x36, 0x97, 0x1a,
-	0x88, 0x6a, 0x66, 0x00,
-	0xac, 0x6a, 0xa2, 0x17,
-	0x00, 0x65, 0x9f, 0x17,
-	0xff, 0xa3, 0x38, 0x02,
-	0x39, 0x6a, 0x66, 0x00,
-	0xa4, 0x6a, 0xa5, 0x17,
-	0xff, 0x38, 0xac, 0x1a,
-	0x80, 0x02, 0x02, 0x00,
-	0xff, 0x6a, 0x8c, 0x00,
-	0xff, 0x6a, 0x8d, 0x00,
-	0xff, 0x6a, 0x8e, 0x00,
-	0x00, 0x65, 0x9f, 0x17,
-	0xe7, 0x35, 0x35, 0x02,
-	0x01, 0x38, 0xae, 0x18,
-	0xbf, 0x35, 0x35, 0x02,
-	0x00, 0x35, 0x61, 0x17,
-	0x80, 0x02, 0xc6, 0x1a,
-	0xff, 0x65, 0xc0, 0x1e,
-	0xff, 0x38, 0x38, 0x06,
-	0xff, 0x38, 0xc0, 0x1e,
-	0xff, 0x6a, 0x64, 0x02,
-	0x08, 0x39, 0x39, 0x06,
-	0x00, 0x3a, 0x3a, 0x08,
-	0x88, 0x6a, 0x66, 0x00,
-	0x39, 0x6a, 0xa5, 0x17,
-	0x08, 0x6a, 0x8c, 0x00,
-	0xff, 0x6a, 0x8d, 0x02,
-	0xff, 0x6a, 0x8e, 0x02,
-	0x0d, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x88, 0x6a, 0xc9, 0x17,
-	0x00, 0x65, 0x9f, 0x17,
-	0x10, 0x0c, 0xa5, 0x1e,
-	0xff, 0x08, 0xa9, 0x02,
-	0xff, 0x09, 0xaa, 0x02,
-	0xff, 0x0a, 0xab, 0x02,
-	0xff, 0x38, 0xa8, 0x02,
-	0x10, 0x36, 0x36, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x7f, 0x02, 0x02, 0x02,
-	0xe1, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x00, 0x65, 0x6c, 0x17,
-	0x88, 0x6a, 0x66, 0x00,
-	0xb4, 0x6a, 0xa4, 0x17,
-	0xff, 0x6a, 0x8d, 0x02,
-	0xff, 0x6a, 0x8e, 0x02,
-	0x00, 0x65, 0x9f, 0x17,
-	0x3d, 0x6a, 0x61, 0x17,
-	0x00, 0x65, 0x82, 0x10,
-	0x00, 0x65, 0x6c, 0x17,
-	0xff, 0x06, 0xa2, 0x02,
-	0x00, 0x65, 0x82, 0x10,
-	0xff, 0x34, 0x65, 0x02,
-	0x80, 0x65, 0xe6, 0x18,
-	0x0f, 0xa1, 0x65, 0x02,
-	0x07, 0xa1, 0x65, 0x02,
-	0x40, 0xa0, 0x64, 0x02,
-	0x00, 0x65, 0x65, 0x00,
-	0x80, 0x65, 0x65, 0x00,
-	0x80, 0xa0, 0xde, 0x1e,
-	0xff, 0x65, 0x06, 0x02,
-	0x00, 0x65, 0xe7, 0x10,
-	0x20, 0xa0, 0xe9, 0x1e,
-	0xff, 0x65, 0x06, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xa0, 0x3d, 0xef, 0x18,
-	0x23, 0xa0, 0x06, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xa0, 0x3d, 0xef, 0x18,
-	0x00, 0xb9, 0xe9, 0x10,
-	0xff, 0x65, 0xe9, 0x18,
-	0xa1, 0x6a, 0x91, 0x00,
-	0x10, 0x51, 0xef, 0x1c,
-	0x40, 0x6a, 0x0c, 0x00,
-	0xff, 0x65, 0x06, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xa0, 0x3d, 0xef, 0x18,
-	0x10, 0x3d, 0x03, 0x00,
-	0x00, 0x65, 0xd4, 0x10,
-	0x40, 0x6a, 0x0c, 0x00,
-	0xff, 0x34, 0x52, 0x02,
-	0x80, 0x34, 0xf3, 0x18,
-	0x7f, 0xa0, 0xa0, 0x02,
-	0x08, 0x6a, 0x34, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x64, 0x6a, 0x59, 0x17,
-	0x80, 0x64, 0x2f, 0x1b,
-	0x04, 0x64, 0x22, 0x1d,
-	0x02, 0x64, 0x25, 0x1d,
-	0x00, 0x6a, 0x02, 0x1d,
-	0x03, 0x64, 0x2d, 0x1d,
-	0x01, 0x64, 0x20, 0x1d,
-	0x07, 0x64, 0x50, 0x1d,
-	0x08, 0x64, 0x00, 0x1d,
-	0x11, 0x6a, 0x91, 0x00,
-	0x07, 0x6a, 0x52, 0x17,
-	0xff, 0x06, 0x6a, 0x02,
-	0x00, 0x65, 0x82, 0x10,
-	0xff, 0xa8, 0x04, 0x1b,
-	0xff, 0xa2, 0x0f, 0x1f,
-	0x01, 0x6a, 0x35, 0x00,
-	0x00, 0xb9, 0xb3, 0x17,
-	0xff, 0xa2, 0x0f, 0x1f,
-	0x71, 0x6a, 0x91, 0x00,
-	0x40, 0x51, 0x0f, 0x19,
-	0x0d, 0x6a, 0x35, 0x00,
-	0x00, 0xb9, 0xb3, 0x17,
-	0xff, 0x3e, 0xba, 0x02,
-	0xff, 0x90, 0x3e, 0x02,
-	0x00, 0x65, 0x20, 0x16,
-	0x00, 0x65, 0x8b, 0x10,
-	0x20, 0xa0, 0x16, 0x1b,
-	0xff, 0x37, 0x64, 0x02,
-	0x00, 0x6a, 0x93, 0x17,
-	0x01, 0x6a, 0x93, 0x00,
-	0xff, 0x6a, 0x99, 0x00,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0xff, 0x4f, 0x64, 0x02,
-	0x01, 0x6a, 0x93, 0x17,
-	0x01, 0x6a, 0x93, 0x00,
-	0xff, 0xb9, 0x99, 0x02,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x01, 0x4f, 0x4f, 0x06,
-	0x02, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0xd5, 0x17,
-	0x00, 0x65, 0x8b, 0x10,
-	0x41, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0x04, 0xa0, 0xa0, 0x00,
-	0x00, 0x65, 0xe1, 0x17,
-	0x00, 0x65, 0x8b, 0x10,
-	0x10, 0x36, 0x00, 0x1f,
-	0xff, 0x38, 0xa3, 0x02,
-	0xa4, 0x6a, 0x66, 0x00,
-	0x39, 0x6a, 0xa5, 0x17,
-	0xac, 0x6a, 0x66, 0x00,
-	0x14, 0x6a, 0xa5, 0x17,
-	0xa9, 0x6a, 0xa6, 0x17,
-	0x00, 0x65, 0x00, 0x11,
-	0xef, 0x36, 0x36, 0x02,
-	0x00, 0x65, 0x00, 0x11,
-	0x0f, 0x64, 0x64, 0x02,
-	0x07, 0x64, 0x64, 0x02,
-	0x00, 0x37, 0x37, 0x00,
-	0x00, 0x65, 0x8b, 0x17,
-	0xff, 0x51, 0x37, 0x1d,
-	0x20, 0x36, 0x3f, 0x1f,
-	0x00, 0x90, 0x7d, 0x17,
-	0x00, 0x65, 0x40, 0x11,
-	0xff, 0x06, 0x6a, 0x02,
-	0x00, 0x65, 0x9b, 0x17,
-	0xe0, 0x3d, 0x4d, 0x19,
-	0x20, 0x12, 0x4d, 0x19,
-	0x51, 0x6a, 0x54, 0x17,
-	0xff, 0x51, 0x90, 0x02,
-	0x20, 0xa0, 0x4d, 0x1f,
-	0x00, 0x90, 0x7d, 0x17,
-	0x00, 0x65, 0x7a, 0x17,
-	0xff, 0x37, 0x64, 0x02,
-	0x00, 0xa1, 0x49, 0x19,
-	0x04, 0xa0, 0x49, 0x1f,
-	0xfb, 0xa0, 0xa0, 0x02,
-	0x80, 0x36, 0x36, 0x00,
-	0x80, 0xa0, 0x00, 0x1f,
-	0x7f, 0xa0, 0xa0, 0x02,
-	0xff, 0x6a, 0x52, 0x17,
-	0x00, 0x65, 0x00, 0x11,
-	0x04, 0xa0, 0x4c, 0x1f,
-	0x00, 0x65, 0xe1, 0x17,
-	0x00, 0x65, 0x4d, 0x11,
-	0x00, 0x65, 0xd5, 0x17,
-	0x31, 0x6a, 0x91, 0x00,
-	0x0c, 0x6a, 0x52, 0x17,
-	0x00, 0x65, 0x00, 0x11,
-	0x61, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x00, 0x11,
-	0x10, 0x3d, 0x03, 0x00,
-	0xff, 0x65, 0x34, 0x03,
-	0xff, 0x06, 0x6a, 0x02,
-	0x01, 0x0c, 0x55, 0x1f,
-	0x04, 0x0c, 0x55, 0x1b,
-	0xe0, 0x03, 0x3d, 0x02,
-	0xe0, 0x3d, 0x5f, 0x19,
-	0xff, 0x65, 0x66, 0x02,
-	0xff, 0x12, 0x6d, 0x03,
-	0xff, 0x06, 0x6a, 0x03,
-	0xff, 0x65, 0x06, 0x02,
-	0x02, 0x0b, 0x5d, 0x1f,
-	0xff, 0x6a, 0x6a, 0x03,
-	0xd1, 0x6a, 0x91, 0x00,
-	0x00, 0x65, 0x82, 0x10,
-	0xff, 0x65, 0x93, 0x02,
-	0x01, 0x0b, 0x69, 0x1b,
-	0x10, 0x0c, 0x62, 0x1f,
-	0x04, 0x0b, 0x66, 0x1b,
-	0xff, 0x6a, 0x65, 0x02,
-	0x04, 0x93, 0x68, 0x1b,
-	0x01, 0x94, 0x67, 0x1f,
-	0x10, 0x94, 0x68, 0x1b,
-	0xc7, 0x93, 0x93, 0x02,
-	0x38, 0x93, 0x6a, 0x1b,
-	0xff, 0x6a, 0x6a, 0x03,
-	0x80, 0x36, 0x6b, 0x1b,
-	0x21, 0x6a, 0x91, 0x01,
-	0xff, 0x65, 0x90, 0x02,
-	0xff, 0x51, 0x72, 0x19,
-	0xff, 0x37, 0x64, 0x02,
-	0xa1, 0x6a, 0x77, 0x11,
-	0xff, 0x51, 0x64, 0x02,
-	0xb9, 0x6a, 0x77, 0x11,
-	0xff, 0xba, 0x79, 0x1d,
-	0xff, 0xba, 0x90, 0x02,
-	0xff, 0x65, 0x65, 0x06,
-	0x00, 0x6c, 0x74, 0x19,
-	0xff, 0x90, 0x65, 0x03,
-	0xff, 0x6a, 0x65, 0x01,
-	0x20, 0x36, 0x88, 0x1f,
-	0x00, 0x90, 0x6e, 0x17,
-	0xff, 0x65, 0x88, 0x1d,
-	0xff, 0xba, 0x82, 0x1d,
-	0xff, 0xbb, 0x66, 0x02,
-	0xff, 0xba, 0x90, 0x02,
-	0xff, 0x66, 0xbb, 0x02,
-	0xff, 0x65, 0x90, 0x02,
-	0xff, 0xbb, 0x87, 0x1d,
-	0xff, 0xba, 0x66, 0x02,
-	0xff, 0xbb, 0x90, 0x02,
-	0xff, 0x66, 0xba, 0x02,
-	0xff, 0x65, 0x90, 0x03,
-	0xff, 0xba, 0x3f, 0x03,
-	0x00, 0x6a, 0xd8, 0x17,
-	0x0d, 0x6a, 0x35, 0x00,
-	0x00, 0x51, 0xb3, 0x11,
-	0xff, 0x3f, 0x96, 0x1d,
-	0xff, 0x6a, 0x51, 0x00,
-	0x00, 0x3f, 0x6e, 0x17,
-	0xff, 0x65, 0x96, 0x1d,
-	0x20, 0x36, 0x36, 0x00,
-	0x20, 0xa0, 0x92, 0x1b,
-	0xff, 0xb9, 0x51, 0x03,
-	0xff, 0x6a, 0x51, 0x01,
-	0xff, 0x65, 0x66, 0x02,
-	0x45, 0x6a, 0xab, 0x17,
-	0x01, 0x6a, 0x8c, 0x01,
-	0xff, 0x37, 0x64, 0x02,
-	0x00, 0x6a, 0x93, 0x17,
-	0x0d, 0x6a, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0xff, 0x99, 0x51, 0x03,
-	0x01, 0x0c, 0x9b, 0x1f,
-	0x04, 0x0c, 0x9b, 0x1b,
-	0xe0, 0x03, 0x3d, 0x02,
-	0xff, 0x3d, 0x03, 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, 0x64, 0x66, 0x0a,
-	0x55, 0x64, 0x64, 0x0a,
-	0x00, 0x6c, 0x88, 0x06,
-	0xff, 0x66, 0x64, 0x02,
-	0x00, 0x6c, 0x89, 0x08,
-	0xff, 0x6a, 0x64, 0x02,
-	0x00, 0x6c, 0x8a, 0x08,
-	0x00, 0x6c, 0x8b, 0x08,
-	0xff, 0x6a, 0x8d, 0x02,
-	0xff, 0x6a, 0x8e, 0x03,
-	0xff, 0x65, 0x64, 0x02,
-	0x41, 0x6a, 0xa9, 0x17,
-	0x1c, 0x6a, 0x8c, 0x00,
-	0xff, 0x35, 0x93, 0x02,
-	0x04, 0x35, 0xc3, 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, 0xba, 0x19,
-	0x0a, 0x93, 0x93, 0x00,
-	0x00, 0x65, 0xd1, 0x17,
-	0x04, 0x35, 0x6b, 0x1f,
-	0xa0, 0x6a, 0xc9, 0x17,
-	0x00, 0x65, 0xca, 0x17,
-	0x00, 0x65, 0xca, 0x17,
-	0x00, 0x65, 0xca, 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, 0xd1, 0x1f,
-	0xf7, 0x93, 0x93, 0x02,
-	0x08, 0x93, 0xd3, 0x1b,
-	0xff, 0x6a, 0x6a, 0x03,
-	0xff, 0x40, 0xba, 0x02,
-	0xff, 0x90, 0x40, 0x02,
-	0xff, 0x6a, 0xb9, 0x01,
-	0xff, 0x40, 0xdf, 0x19,
-	0xff, 0x3f, 0xdb, 0x19,
-	0xff, 0x6a, 0x65, 0x01,
-	0xff, 0x3f, 0x90, 0x02,
-	0x01, 0x6a, 0x35, 0x00,
-	0x00, 0xb9, 0xb3, 0x17,
-	0x00, 0x90, 0x7d, 0x11,
-	0xff, 0x40, 0x90, 0x02,
-	0xff, 0xba, 0x40, 0x03,
-	0xff, 0x6a, 0xbb, 0x00,
-	0xff, 0x3f, 0xba, 0x02,
-	0xff, 0x90, 0x3f, 0x02,
-	0xff, 0xba, 0x6b, 0x1d,
-	0xff, 0xba, 0x90, 0x02,
-	0xff, 0x3f, 0xbb, 0x02,
-	0xff, 0x3f, 0x90, 0x03,
-};
-#define	WIDE            	0x20
-#define	ULTRA           	0x10
-#define	SCB_PAGING      	0x8
-#define	TWIN_CHANNEL    	0x4
-#define	TARGET_MODE     	0x2
-struct sequencer_patch {
-	int	options;
-	int	negative;
-	int	begin;
-	int	end;
-} sequencer_patches[] = {
-	{ 0x00000002, 0, 0x001, 0x002 },
-	{ 0x00000002, 1, 0x002, 0x003 },
-	{ 0x00000004, 0, 0x009, 0x00d },
-	{ 0x00000008, 0, 0x012, 0x013 },
-	{ 0x00000008, 1, 0x018, 0x019 },
-	{ 0x00000004, 0, 0x020, 0x024 },
-	{ 0x00000010, 0, 0x02a, 0x031 },
-	{ 0x00000002, 0, 0x038, 0x071 },
-	{ 0x00000020, 0, 0x0d6, 0x0d7 },
-	{ 0x00000020, 1, 0x0d7, 0x0d8 },
-	{ 0x00000020, 0, 0x12f, 0x130 },
-	{ 0x00000020, 1, 0x130, 0x131 },
-	{ 0x00000008, 0, 0x134, 0x135 },
-	{ 0x00000008, 1, 0x13c, 0x13f },
-	{ 0x00000008, 0, 0x13f, 0x140 },
-	{ 0x00000002, 0, 0x15c, 0x15f },
-	{ 0x00000008, 0, 0x1d5, 0x1d7 },
-	{ 0x00000008, 0, 0x1d8, 0x1e1 },
-	{ 0x00000000, 0, 0x000, 0x000 }
-};
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/dtc.c linux/drivers/scsi/dtc.c
--- v2.0.35/linux/drivers/scsi/dtc.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/dtc.c	Sun Nov 15 10:33:08 1998
@@ -176,7 +176,7 @@
       base = NULL;
 
       if (overrides[current_override].address)
-	 base = overrides[current_override].address;
+	 base = (unsigned char *)overrides[current_override].address;
       else
 	 for (; !base && (current_base < NO_BASES); ++current_base) {
 #if (DTCDEBUG & DTCDEBUG_INIT)
@@ -184,9 +184,9 @@
 #endif
 	 for (sig = 0; sig < NO_SIGNATURES; ++sig)
 	    if (!bases[current_base].noauto && !memcmp
-	      (bases[current_base].address + signatures[sig].offset,
+	      ((unsigned char *)(bases[current_base].address + signatures[sig].offset),
 	      signatures[sig].string, strlen(signatures[sig].string))) {
-	    base = bases[current_base].address;
+	    base = (unsigned char *)bases[current_base].address;
 #if (DTCDEBUG & DTCDEBUG_INIT)
 	    printk("scsi-dtc : detected board.\n");
 #endif
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v2.0.35/linux/drivers/scsi/eata.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/eata.c	Sun Nov 15 10:33:08 1998
@@ -1,6 +1,25 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *      26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
+ *        + Added command line option (rs:[y|n]) to reverse the scan order
+ *          of PCI boards. The default is rs:y, which reverses the BIOS order
+ *          while registering PCI boards. The default value rs:y generates
+ *          the same order of all previous revisions of this driver.
+ *          Pls. note that "BIOS order" might have been reversed itself
+ *          after the 2.1.9x PCI modifications in the linux kernel.
+ *          The rs value is ignored when the explicit list of addresses
+ *          is used by the "eata=port0,port1,..." command line option.
+ *        + Added command line option (et:[y|n]) to force use of extended
+ *          translation (255 heads, 63 sectors) as disk geometry.
+ *          The default is et:n, which uses the disk geometry returned
+ *          by scsicam_bios_param. The default value et:n is compatible with
+ *          all previous revisions of this driver.
+ *
+ *      28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
+ *          Increased busy timeout from 10 msec. to 200 msec. while
+ *          processing interrupts.
+ *
  *      16 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
  *          Improved abort handling during the eh recovery process.
  *
@@ -9,7 +28,7 @@
  *          abort and reset routines.
  *          Added command line options (eh:[y|n]) to choose between
  *          new_eh_code and the old scsi code.
- *          If linux verion >= 2.1.101 the default is eh:y, while the eh
+ *          If linux version >= 2.1.101 the default is eh:y, while the eh
  *          option is ignored for previous releases and the old scsi code
  *          is used.
  *
@@ -189,6 +208,10 @@
  *  PM3222     -  SmartRAID Adapter for EISA (PM3222W is 16-bit wide SCSI)
  *  PM3224     -  SmartRAID Adapter for PCI  (PM3224W is 16-bit wide SCSI)
  *
+ *  The above list is just an indication: as a matter of fact all DPT
+ *  boards using the EATA/DMA protocol are supported by this driver,
+ *  since they use exactely the same programming interface.
+ *
  *  The DPT PM2001 provides only the EATA/PIO interface and hence is not
  *  supported by this driver.
  *
@@ -251,6 +274,10 @@
  *
  *  eh:y  use new scsi code (linux 2.2 only);
  *  eh:n  use old scsi code;
+ *  et:y  force use of extended translation (255 heads, 63 sectors);
+ *  et:n  use disk geometry detected by scsicam_bios_param;
+ *  rs:y  reverse scan order while detecting PCI boards;
+ *  rs:n  use BIOS order while detecting PCI boards;
  *  lc:y  enables linked commands;
  *  lc:n  disables linked commands;
  *  tc:y  enables tagged commands;
@@ -261,15 +288,16 @@
  *  tm:3  use only ordered queue tags;
  *  mq:xx set the max queue depth to the value xx (2 <= xx <= 32).
  *
- *  The default value is: "eata=lc:n,tc:n,mq:16,tm:0". An example using
- *  the list of detection probes could be:
- *  "eata=0x7410,0x230,lc:y,tc:n,mq:4,eh:n".
+ *  The default value is: "eata=lc:n,tc:n,mq:16,tm:0,et:n,rs:n".
+ *  An example using the list of detection probes could be:
+ *  "eata=0x7410,0x230,lc:y,tc:n,mq:4,eh:n,et:n".
  *
  *  When loading as a module, parameters can be specified as well.
  *  The above example would be (use 1 in place of y and 0 in place of n):
  *
  *  modprobe eata io_port=0x7410,0x230 linked_comm=1 tagged_comm=0 \
- *                max_queue_depth=4 tag_mode=0 use_new_eh_code=0
+ *                max_queue_depth=4 tag_mode=0 use_new_eh_code=0 \
+ *                ext_tran=0 rev_scan=1
  *
  *  ----------------------------------------------------------------------------
  *  In this implementation, linked commands are designed to work with any DISK
@@ -303,6 +331,19 @@
  *        When the driver detects a batch including overlapping requests
  *        (a really rare event) strict serial (pid) order is enforced.
  *  ----------------------------------------------------------------------------
+ *  The extended translation option (et:y) is useful when using large physical
+ *  disks/arrays. It could also be useful when switching between Adaptec boards
+ *  and DPT boards without reformatting the disk.
+ *  When a boot disk is partitioned with extended translation, in order to
+ *  be able to boot it with a DPT board is could be necessary to add to
+ *  lilo.conf additional commands as in the following example:
+ *
+ *  fix-table
+ *  disk=/dev/sda bios=0x80 sectors=63 heads=128 cylindres=546
+ *
+ *  where the above geometry should be replaced with the one reported at
+ *  power up by the DPT controller.
+ *  ----------------------------------------------------------------------------
  *
  *  The boards are named EATA0, EATA1,... according to the detection order.
  *
@@ -326,6 +367,8 @@
 MODULE_PARM(max_queue_depth, "i");
 MODULE_PARM(tag_mode, "i");
 MODULE_PARM(use_new_eh_code, "i");
+MODULE_PARM(ext_tran, "i");
+MODULE_PARM(rev_scan, "i");
 MODULE_AUTHOR("Dario Ballabio");
 #endif
 
@@ -409,6 +452,7 @@
 #undef  DEBUG_RESET
 #undef  DEBUG_GENERATE_ERRORS
 #undef  DEBUG_GENERATE_ABORTS
+#undef  DEBUG_GEOMETRY
 
 #define MAX_ISA 4
 #define MAX_VESA 0
@@ -659,6 +703,8 @@
 static int setup_done = FALSE;
 static int link_statistics = 0;
 static int tag_mode = TAG_MIXED;
+static int ext_tran = FALSE;
+static int rev_scan = TRUE;
 
 #if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE)
 static int tagged_comm = TRUE;
@@ -1063,9 +1109,9 @@
 
    if (j == 0) {
       printk("EATA/DMA 2.0x: Copyright (C) 1994-1998 Dario Ballabio.\n");
-      printk("%s config options -> tc:%c, lc:%c, mq:%d, eh:%c.\n",
-             driver_name, tag_type, YESNO(linked_comm),
-             max_queue_depth, YESNO(use_new_eh_code));
+      printk("%s config options -> tc:%c, lc:%c, mq:%d, eh:%c, rs:%c, et:%c.\n",
+             driver_name, tag_type, YESNO(linked_comm), max_queue_depth,
+             YESNO(use_new_eh_code), YESNO(rev_scan), YESNO(ext_tran));
       }
 
    printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d.\n",
@@ -1128,6 +1174,8 @@
       else if (!strncmp(cur, "mq:", 3))  max_queue_depth = val;
       else if (!strncmp(cur, "ls:", 3))  link_statistics = val;
       else if (!strncmp(cur, "eh:", 3))  use_new_eh_code = val;
+      else if (!strncmp(cur, "et:", 3))  ext_tran = val;
+      else if (!strncmp(cur, "rs:", 3))  rev_scan = val;
 
       if ((cur = strchr(cur, ','))) ++cur;
       }
@@ -1161,8 +1209,8 @@
       if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
              continue;
 
-      /* Reverse the returned address order */
-      io_port[MAX_INT_PARAM + MAX_PCI - k] =
+      /* Order addresses according to rev_scan value */
+      io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
              (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
       }
 
@@ -1189,8 +1237,8 @@
       if ((addr & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
              continue;
 
-      /* Reverse the returned address order */
-      io_port[MAX_INT_PARAM + MAX_PCI - k] =
+      /* Order addresses according to rev_scan value */
+      io_port[MAX_INT_PARAM + (rev_scan ? (MAX_PCI - k) : (1 + k))] =
              (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
       }
 
@@ -1842,6 +1890,23 @@
 
 #endif /* new_eh_code */
 
+int eata2x_biosparam(Disk *disk, kdev_t dev, int *dkinfo) {
+   int size = disk->capacity;
+
+   if (ext_tran || (scsicam_bios_param(disk, dev, dkinfo) < 0)) {
+      dkinfo[0] = 255;
+      dkinfo[1] = 63;
+      dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
+      }
+
+#if defined (DEBUG_GEOMETRY)
+   printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name,
+           dkinfo[0], dkinfo[1], dkinfo[2]);
+#endif
+
+   return FALSE;
+}
+
 static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
                  unsigned int rev) {
    unsigned int i, j, k, y;
@@ -2020,7 +2085,7 @@
                         HD(j)->iocount);
 
    /* Check if this board is still busy */
-   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+   if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) {
       reg = inb(sh[j]->io_port + REG_STATUS);
       printk("%s: ihdlr, busy timeout error,  irq %d, reg 0x%x, count %d.\n",
              BN(j), irq, reg, HD(j)->iocount);
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v2.0.35/linux/drivers/scsi/eata.h	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/eata.h	Sun Nov 15 10:33:08 1998
@@ -14,8 +14,9 @@
 int eata2x_old_abort(Scsi_Cmnd *);
 int eata2x_reset(Scsi_Cmnd *);
 int eata2x_old_reset(Scsi_Cmnd *, unsigned int);
+int eata2x_biosparam(Disk *, kdev_t, int *);
 
-#define EATA_VERSION "4.31.00"
+#define EATA_VERSION "4.33.00"
 
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
 
@@ -32,7 +33,7 @@
                 eh_device_reset_handler: NULL,                               \
                 eh_bus_reset_handler:    NULL,                               \
                 eh_host_reset_handler:   eata2x_reset,                       \
-                bios_param:              scsicam_bios_param,                 \
+                bios_param:              eata2x_biosparam,                   \
                 this_id:                 7,                                  \
                 unchecked_isa_dma:       1,                                  \
                 use_clustering:          ENABLE_CLUSTERING,                  \
@@ -48,7 +49,7 @@
                 queuecommand:            eata2x_queuecommand,                \
                 abort:                   eata2x_old_abort,                   \
                 reset:                   eata2x_old_reset,                   \
-                bios_param:              scsicam_bios_param,                 \
+                bios_param:              eata2x_biosparam,                   \
                 this_id:                 7,                                  \
                 unchecked_isa_dma:       1,                                  \
                 use_clustering:          ENABLE_CLUSTERING                   \
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v2.0.35/linux/drivers/scsi/eata_dma.c	Mon Oct 28 14:30:43 1996
+++ linux/drivers/scsi/eata_dma.c	Sun Nov 15 10:33:09 1998
@@ -547,7 +547,7 @@
 	ccb->DataIn = TRUE;	/* Input mode  */
     }
 
-    /* FIXME: This will will have to be changed once the midlevel driver 
+    /* FIXME: This will have to be changed once the midlevel driver 
      *        allows different HBA IDs on every channel.
      */
     if (cmd->target == sh->this_id) 
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth.c linux/drivers/scsi/gdth.c
--- v2.0.35/linux/drivers/scsi/gdth.c	Mon Aug  4 17:15:07 1997
+++ linux/drivers/scsi/gdth.c	Sun Nov 15 10:33:09 1998
@@ -2,7 +2,7 @@
  * GDT ISA/EISA/PCI Disk Array Controller driver for Linux              *
  *                                                                      *
  * gdth.c                                                               *
- * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner *
+ * Copyright (C) 1995-98 ICP vortex Computersysteme GmbH, Achim Leubner *
  *                                                                      *
  * <achim@vortex.de>                                                    *
  *                                                                      *
@@ -20,9 +20,44 @@
  * 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                                *
+ * Tested with Linux 1.2.13, ..., 2.1.103                               *
  *                                                                      *
  * $Log: gdth.c,v $
+ * Revision 1.16  1998/09/28 16:08:46  achim
+ * GDT_PCIMPR: DPMEM remapping, if required
+ * mdelay() added
+ *
+ * Revision 1.15  1998/06/03 14:54:06  achim
+ * gdth_delay(), gdth_flush() implemented
+ * Bugfix: gdth_release() changed
+ *
+ * Revision 1.14  1998/05/22 10:01:17  achim
+ * mj: pcibios_strerror() removed
+ * Improved SMP support (if version >= 2.1.95)
+ * gdth_halt(): halt_called flag added (if version < 2.1)
+ *
+ * Revision 1.13  1998/04/16 09:14:57  achim
+ * Reserve drives (for raw service) implemented
+ * New error handling code enabled
+ * Get controller name from board_info() IOCTL
+ * Final round of PCI device driver patches by Martin Mares
+ *
+ * Revision 1.12  1998/03/03 09:32:37  achim
+ * Fibre channel controller support added
+ *
+ * Revision 1.11  1998/01/27 16:19:14  achim
+ * SA_SHIRQ added
+ * add_timer()/del_timer() instead of GDTH_TIMER
+ * scsi_add_timer()/scsi_del_timer() instead of SCSI_TIMER
+ * New error handling included
+ *
+ * Revision 1.10  1997/10/31 12:29:57  achim
+ * Read heads/sectors from host drive
+ *
+ * Revision 1.9  1997/09/04 10:07:25  achim
+ * IO-mapping with virt_to_bus(), readb(), writeb(), ...
+ * register_reboot_notifier() to get a notify on shutdown used
+ *
  * Revision 1.8  1997/04/02 12:14:30  achim
  * Version 1.00 (see gdth.h), tested with kernel 2.0.29
  *
@@ -53,7 +88,7 @@
  * Initial revision
  *
  *
- * $Id: gdth.c,v 1.8 1997/04/02 12:14:30 achim Exp $ 
+ * $Id: gdth.c,v 1.16 1998/09/28 16:08:46 achim Exp $ 
  ************************************************************************/
 
 #ifdef MODULE
@@ -64,7 +99,6 @@
 #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>
@@ -74,10 +108,18 @@
 #include <linux/proc_fs.h>
 #include <linux/time.h>
 #include <linux/timer.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/reboot.h>
+#else
+#include <linux/bios32.h>
+#endif
 
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/io.h>
+#if LINUX_VERSION_CODE >= 0x02015F
+#include <asm/spinlock.h>
+#endif
 
 #if LINUX_VERSION_CODE >= 0x010300
 #include <linux/blk.h>
@@ -90,7 +132,34 @@
 
 #include "gdth.h"
 
-#if LINUX_VERSION_CODE >= 0x010346
+/****************************************************************************/
+
+/* 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;
+
+/* Reserve drives for raw service: Fill the following structure with the
+ * appropriate values: Controller number, Channel, Target ID 
+ */
+static gdth_reserve_str reserve_list[] = {
+    /* { 0, 1, 4 },		Example: Controller 0, Channel B, ID 4 */
+    { 0xff, 0xff, 0xff }	/* end of list */
+};
+
+/****************************************************************************/
+
+#if LINUX_VERSION_CODE >= 0x02015F
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+static void do_gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+#elif 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);
@@ -116,9 +185,9 @@
 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 int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha);
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha);
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha);
 
 static void gdth_enable_int(int hanum);
 static int gdth_get_status(unchar *pIStatus,int irq);
@@ -128,28 +197,25 @@
 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 int gdth_search_drives(int hanum);
+
+static void *gdth_mmap(ulong paddr, ulong size);
+static void gdth_munmap(void *addr);
 
 static const char *gdth_ctr_name(int hanum);
+
+static void gdth_flush(int hanum);
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf);
+#else
+static int halt_called = FALSE;
 void gdth_halt(void);
+#endif
 
 #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
+#define LOGEN           sys_syslog(7,NULL,0)
 
 #ifdef __SERIAL__
 #define MAX_SERBUF 160
@@ -213,14 +279,14 @@
     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}}
+#define TRACE(a)    {if (DebugState==1) {ser_printk a;}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {ser_printk a;}}
+#define TRACE3(a)   {if (DebugState!=0) {ser_printk a;}}
 
 #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}}
+#define TRACE(a)    {if (DebugState==1) {LOGEN;printk a;}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {LOGEN;printk a;}}
+#define TRACE3(a)   {if (DebugState!=0) {LOGEN;printk a;}}
 #endif
 
 #else /* !DEBUG */
@@ -229,15 +295,13 @@
 #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 ! */
+static struct timer_list gdth_timer;
 #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]))
 
@@ -246,6 +310,67 @@
 #define CMDDATA(a)      (&((gdth_ext_str *)((a)->hostdata))->cmdext)
 #define DMADATA(a)      (&((gdth_ext_str *)((a)->hostdata))->dmaext)
 
+
+#if LINUX_VERSION_CODE < 0x010300
+static void *gdth_mmap(ulong paddr, ulong size) 
+{
+    if (paddr >= high_memory)
+	return NULL; 
+    else
+	return (void *)paddr;
+}
+static void gdth_munmap(void *addr) 
+{
+}
+inline ulong virt_to_phys(volatile void *addr)
+{
+    return (ulong)addr;
+}
+inline void *phys_to_virt(ulong addr)
+{
+    return (void *)addr;
+}
+#define virt_to_bus		virt_to_phys
+#define bus_to_virt		phys_to_virt
+#define readb(addr)		(*(volatile unchar *)(addr))
+#define readw(addr)		(*(volatile ushort *)(addr))
+#define readl(addr)		(*(volatile ulong *)(addr))
+#define writeb(b,addr)		(*(volatile unchar *)(addr) = (b))
+#define writew(b,addr)		(*(volatile ushort *)(addr) = (b))
+#define writel(b,addr)		(*(volatile ulong *)(addr) = (b))
+#define memset_io(a,b,c)	memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c)	memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c)	memcpy((void *)(a),(b),(c))
+
+#elif LINUX_VERSION_CODE < 0x020100
+static int remapped = FALSE;
+static void *gdth_mmap(ulong paddr, ulong size) 
+{
+    if ( paddr >= high_memory) {
+	remapped = TRUE;
+	return vremap(paddr, size);
+    } else {
+	return (void *)paddr; 
+    }
+}
+static void gdth_munmap(void *addr) 
+{
+    if (remapped)
+	vfree(addr);
+    remapped = FALSE;
+}
+#else
+static void *gdth_mmap(ulong paddr, ulong size) 
+{ 
+    return ioremap(paddr, size); 
+}
+static void gdth_munmap(void *addr) 
+{
+    return iounmap(addr);
+}
+#endif
+
+
 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 */
@@ -253,6 +378,7 @@
 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 int	gdth_ctr_released = 0;			/* gdth_release() */
 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 */
@@ -289,17 +415,14 @@
     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;
+/* __initfunc, __initdata macros */
+#if LINUX_VERSION_CODE >= 0x020126
+#include <linux/init.h>
+#else
+#define __initfunc(A) A
+#define __initdata
+#define __init
+#endif
 
 /* /proc support */
 #if LINUX_VERSION_CODE >= 0x010300
@@ -312,10 +435,31 @@
 #include "gdth_proc.c"
 #endif
 
+#if LINUX_VERSION_CODE >= 0x020100
+/* notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block gdth_notifier = {
+    gdth_halt, NULL, 0
+};
+#endif
+
+static void gdth_delay(int milliseconds)
+{
+    if (milliseconds == 0) {
+        udelay(1);
+    } else {
+#if LINUX_VERSION_CODE >= 0x020168
+        mdelay(milliseconds);
+#else
+        int i;
+        for (i = 0; i < milliseconds; ++i) 
+            udelay(1000);
+#endif
+    }
+}
 
 /* controller search and initialization functions */
 
-static int gdth_search_eisa(ushort eisa_adr)
+__initfunc (static int gdth_search_eisa(ushort eisa_adr))
 {
     ulong id;
     
@@ -333,19 +477,23 @@
 }
 
 
-static int gdth_search_isa(ulong bios_adr)
+__initfunc (static int gdth_search_isa(ulong bios_adr))
 {
+    void *addr;
     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;
+    if ((addr = gdth_mmap(bios_adr+BIOS_ID_OFFS, sizeof(ulong))) != NULL) {
+	id = readl(addr);
+	gdth_munmap(addr);
+	if (id == GDT2_ID)                          /* GDT2000 */
+	    return 1;
+    }
     return 0;
 }
 
 
-static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr)
+__initfunc (static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr))
 {
     int error;
     ulong base0,base1,base2;
@@ -353,14 +501,34 @@
     TRACE(("gdth_search_pci() device_id %d, index %d\n",
                  device_id,index));
 
+#if LINUX_VERSION_CODE >= 0x20155
+    if (!pci_present())
+        return 0;
+#else
     if (!pcibios_present())
         return 0;
+#endif
 
     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 >= 0x20155
+    {
+	struct pci_dev *pdev = pci_find_slot(pcistr->bus, pcistr->device_fn);
+	base0 = pdev->base_address[0];
+	base1 = pdev->base_address[1];
+	base2 = pdev->base_address[2];
+	if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+					       PCI_ROM_ADDRESS,
+					       (int *) &pcistr->bios))) {
+	    printk("GDT-PCI: error %d reading configuration space", error);
+	    return -1;
+	}
+	pcistr->irq = pdev->irq;
+    }
+#else
 #if LINUX_VERSION_CODE >= 0x010300
 #define GDTH_BASEP      (int *)
 #else
@@ -380,10 +548,10 @@
                                            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));
+        printk("GDT-PCI: error %d reading configuration space", error);
         return -1;
     }
+#endif
 
     pcistr->device_id = device_id;
     if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B ||   /* GDT6000 or GDT6000B */
@@ -404,7 +572,7 @@
 }
 
 
-static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha,int firsttime)
+__initfunc (static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha))
 {
     ulong retries,id;
     unchar prot_ver,eisacf,i,irq_found;
@@ -418,13 +586,13 @@
     
     outb(0xff,eisa_adr+LDOORREG);
     retries = INIT_RETRIES;
-    JIFFYWAIT(2);
+    gdth_delay(20);
     while (inb(eisa_adr+EDOORREG) != 0xff) {
         if (--retries == 0) {
             printk("GDT-EISA: Initialization error (DEINIT failed)\n");
             return 0;
         }
-        udelay(1000);
+        gdth_delay(1);
         TRACE2(("wait for DEINIT: retries=%ld\n",retries));
     }
     prot_ver = inb(eisa_adr+MAILBOXREG);
@@ -433,7 +601,7 @@
         printk("GDT-EISA: Illegal protocol version\n");
         return 0;
     }
-    ha->brd = (ulong)eisa_adr;
+    ha->bmic = eisa_adr;
     ha->brd_phys = (ulong)eisa_adr >> 12;
 
     outl(0,eisa_adr+MAILBOXREG);
@@ -448,41 +616,38 @@
         outl(1,eisa_adr+MAILBOXREG+8);
         outb(0xfe,eisa_adr+LDOORREG);
         retries = INIT_RETRIES;
-        JIFFYWAIT(2);
+        gdth_delay(20);
         while (inb(eisa_adr+EDOORREG) != 0xfe) {
             if (--retries == 0) {
                 printk("GDT-EISA: Initialization error (get IRQ failed)\n");
                 return 0;
             }
-            udelay(1000);
+            gdth_delay(1);
         }
-        if (firsttime)
-            ha->irq = inb(eisa_adr+MAILBOXREG);
+        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;
-                }
-            }
-        }
+	/* 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 */
@@ -495,7 +660,7 @@
 }
 
        
-static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha,int firsttime)
+__initfunc (static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha))
 {
     register gdt2_dpram_str *dp2_ptr;
     int i;
@@ -504,19 +669,28 @@
 
     TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr));
 
-    ha->brd = bios_adr;
+    ha->brd = gdth_mmap(bios_adr, sizeof(gdt2_dpram_str));
+    if (ha->brd == NULL) {
+	printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
+	return 0;
+    }
     dp2_ptr = (gdt2_dpram_str *)ha->brd;
-    dp2_ptr->io.memlock = 1;            /* switch off write protection */
+    writeb(1, &dp2_ptr->io.memlock);			/* switch off write protection */
     /* reset interface area */
-    memset((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u));
+    memset_io((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u));
+    if (readl(&dp2_ptr->u) != 0) {
+	printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+	gdth_munmap(ha->brd);
+	return 0;
+    }
 
     /* 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;
+    writeb(0xff, &dp2_ptr->io.irqdel);
+    writeb(0x00, &dp2_ptr->io.irqen);
+    writeb(0x00, &dp2_ptr->u.ic.S_Status);
+    writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
 
-    irq_drq = dp2_ptr->io.rq;
+    irq_drq = readb(&dp2_ptr->io.rq);
     for (i=0; i<3; ++i) {
         if ((irq_drq & 1)==0)
             break;
@@ -524,7 +698,7 @@
     }
     ha->drq = gdth_drq_tab[i];
 
-    irq_drq = dp2_ptr->io.rq >> 3;
+    irq_drq = readb(&dp2_ptr->io.rq) >> 3;
     for (i=1; i<5; ++i) {
         if ((irq_drq & 1)==0)
             break;
@@ -533,23 +707,25 @@
     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;
+    writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
+    writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
+    writeb(0, &dp2_ptr->io.event);
     retries = INIT_RETRIES;
-    JIFFYWAIT(2);
-    while (dp2_ptr->u.ic.S_Status != 0xff) {
+    gdth_delay(20);
+    while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
         if (--retries == 0) {
             printk("GDT-ISA: Initialization error (DEINIT failed)\n");
+	    gdth_munmap(ha->brd);
             return 0;
         }
-        udelay(1000);
+        gdth_delay(1);
     }
-    prot_ver = (unchar)dp2_ptr->u.ic.S_Info[0];
-    dp2_ptr->u.ic.Status = 0;
-    dp2_ptr->io.irqdel = 0xff;
+    prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]);
+    writeb(0, &dp2_ptr->u.ic.Status);
+    writeb(0xff, &dp2_ptr->io.irqdel);
     if (prot_ver != PROTOCOL_VERSION) {
         printk("GDT-ISA: Illegal protocol version\n");
+	gdth_munmap(ha->brd);
         return 0;
     }
 
@@ -559,78 +735,84 @@
     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;
+    writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
+    writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
+    writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
+    writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
+    writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
+    writeb(0, &dp2_ptr->io.event);
     retries = INIT_RETRIES;
-    JIFFYWAIT(2);
-    while (dp2_ptr->u.ic.S_Status != 0xfe) {
+    gdth_delay(20);
+    while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
         if (--retries == 0) {
             printk("GDT-ISA: Initialization error\n");
+	    gdth_munmap(ha->brd);
             return 0;
         }
-        udelay(1000);
+        gdth_delay(1);
     }
-    dp2_ptr->u.ic.Status = 0;
-    dp2_ptr->io.irqdel = 0xff;
+    writeb(0, &dp2_ptr->u.ic.Status);
+    writeb(0xff, &dp2_ptr->io.irqdel);
     return 1;
 }
 
 
-static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha,int firsttime)
+__initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha))
 {
     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;
+    int i, found = 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;
-    }
-
+    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));
+        TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+	ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6_dpram_str));
+	if (ha->brd == NULL) {
+	    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+	    return 0;
+	}
         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) {
+        memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u));
+        if (readl(&dp6_ptr->u) != 0) {
             printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+	    gdth_munmap(ha->brd);
             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;
+        writeb(0xff, &dp6_ptr->io.irqdel);
+        writeb(0x00, &dp6_ptr->io.irqen);;
+        writeb(0x00, &dp6_ptr->u.ic.S_Status);
+        writeb(0x00, &dp6_ptr->u.ic.Cmd_Index);
+
+        writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]);
+        writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx);
+        writeb(0, &dp6_ptr->io.event);
         retries = INIT_RETRIES;
-        JIFFYWAIT(2);
-        while (dp6_ptr->u.ic.S_Status != 0xff) {
+        gdth_delay(20);
+        while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) {
             if (--retries == 0) {
                 printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+		gdth_munmap(ha->brd);
                 return 0;
             }
-            udelay(1000);
+            gdth_delay(1);
         }
-        prot_ver = (unchar)dp6_ptr->u.ic.S_Info[0];
-        dp6_ptr->u.ic.S_Status = 0;
-        dp6_ptr->io.irqdel = 0xff;
+        prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]);
+        writeb(0, &dp6_ptr->u.ic.S_Status);
+        writeb(0xff, &dp6_ptr->io.irqdel);
         if (prot_ver != PROTOCOL_VERSION) {
             printk("GDT-PCI: Illegal protocol version\n");
+	    gdth_munmap(ha->brd);
             return 0;
         }
 
@@ -638,35 +820,41 @@
         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;
+        writel(0x00, &dp6_ptr->u.ic.S_Info[0]);
+        writel(0x00, &dp6_ptr->u.ic.S_Info[1]);
+        writel(0x01, &dp6_ptr->u.ic.S_Info[2]);
+        writel(0x00, &dp6_ptr->u.ic.S_Info[3]);
+        writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx);
+        writeb(0, &dp6_ptr->io.event);
         retries = INIT_RETRIES;
-        JIFFYWAIT(2);
-        while (dp6_ptr->u.ic.S_Status != 0xfe) {
+        gdth_delay(20);
+        while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) {
             if (--retries == 0) {
                 printk("GDT-PCI: Initialization error\n");
+		gdth_munmap(ha->brd);
                 return 0;
             }
-            udelay(1000);
+            gdth_delay(1);
         }
-        dp6_ptr->u.ic.S_Status = 0;
-        dp6_ptr->io.irqdel = 0xff;
+        writeb(0, &dp6_ptr->u.ic.S_Status);
+        writeb(0xff, &dp6_ptr->io.irqdel);
 
     } 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));
+	ha->plx = (gdt6c_plx_regs *)pcistr->io;
+	TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n",
+		pcistr->dpmem,(ulong)ha->plx,ha->irq));
+	ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6c_dpram_str));
+	if (ha->brd == NULL) {
+	    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+	    gdth_munmap(ha->brd);
+	    return 0;
+	}
         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) {
+        memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u));
+        if (readl(&dp6c_ptr->u) != 0) {
             printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+	    gdth_munmap(ha->brd);
             return 0;
         }
         
@@ -674,27 +862,29 @@
         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;
+        writeb(0x00, &dp6c_ptr->u.ic.S_Status);
+        writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index);
 
-        dp6c_ptr->u.ic.S_Info[0] = ha->brd;
-        dp6c_ptr->u.ic.S_Cmd_Indx= 0xff;
+        writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]);
+        writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx);
 
         outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
 
         retries = INIT_RETRIES;
-        JIFFYWAIT(2);
-        while (dp6c_ptr->u.ic.S_Status != 0xff) {
+        gdth_delay(20);
+        while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) {
             if (--retries == 0) {
                 printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+		gdth_munmap(ha->brd);
                 return 0;
             }
-            udelay(1000);
+            gdth_delay(1);
         }
-        prot_ver = (unchar)dp6c_ptr->u.ic.S_Info[0];
-        dp6c_ptr->u.ic.Status = 0;
+        prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]);
+        writeb(0, &dp6c_ptr->u.ic.Status);
         if (prot_ver != PROTOCOL_VERSION) {
             printk("GDT-PCI: Illegal protocol version\n");
+	    gdth_munmap(ha->brd);
             return 0;
         }
 
@@ -702,87 +892,92 @@
         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;
+        writel(0x00, &dp6c_ptr->u.ic.S_Info[0]);
+        writel(0x00, &dp6c_ptr->u.ic.S_Info[1]);
+        writel(0x01, &dp6c_ptr->u.ic.S_Info[2]);
+        writel(0x00, &dp6c_ptr->u.ic.S_Info[3]);
+        writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx);
         
         outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
 
         retries = INIT_RETRIES;
-        JIFFYWAIT(2);
-        while (dp6c_ptr->u.ic.S_Status != 0xfe) {
+        gdth_delay(20);
+        while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) {
             if (--retries == 0) {
                 printk("GDT-PCI: Initialization error\n");
+		gdth_munmap(ha->brd);
                 return 0;
             }
-            udelay(1000);
+            gdth_delay(1);
         }
-        dp6c_ptr->u.ic.S_Status = 0;
+        writeb(0, &dp6c_ptr->u.ic.S_Status);
 
     } 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
-        }
+	TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+	ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6m_dpram_str));
+	if (ha->brd == NULL) {
+	    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+	    return 0;
+	}
 
+        /* check and reset interface area */
         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;
-        }
+	writel(DPMEM_MAGIC, &dp6m_ptr->u);
+	if (readl(&dp6m_ptr->u) != DPMEM_MAGIC) {
+	    printk("GDT-PCI: Cannot access DPMEM at 0x%x (shadowed?)\n", 
+		   (int)ha->brd);
+	    found = FALSE;
+	    for (i = 0xC8000; i < 0xE8000; i += 0x4000) {
+		pcibios_write_config_dword( pcistr->bus, pcistr->device_fn,
+					    PCI_BASE_ADDRESS_0, i );
+		gdth_munmap( ha->brd );
+		ha->brd = gdth_mmap(i, sizeof(gdt6m_dpram_str)); 
+		if (ha->brd == NULL) {
+		    printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+		    return 0;
+		}
+		dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+		writel(DPMEM_MAGIC, &dp6m_ptr->u);
+		if (readl(&dp6m_ptr->u) == DPMEM_MAGIC) {
+		    printk("GDT-PCI: Use free address at 0x%x\n",
+			   (int)ha->brd);
+		    found = TRUE;
+		    break;
+		}
+	    }	
+	    if (!found) {
+		printk("GDT-PCI: No free address found!\n");
+		gdth_munmap( ha->brd );
+		return 0;
+	    }
+	}
+        memset_io((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u));
         
         /* 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;
+        writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4,
+	       &dp6m_ptr->i960r.edoor_en_reg);
+        writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+        writeb(0x00, &dp6m_ptr->u.ic.S_Status);
+        writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index);
+
+        writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]);
+        writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx);
+        writeb(1, &dp6m_ptr->i960r.ldoor_reg);
         retries = INIT_RETRIES;
-        JIFFYWAIT(2);
-        while (dp6m_ptr->u.ic.S_Status != 0xff) {
+        gdth_delay(20);
+        while (readb(&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
+		gdth_munmap(ha->brd);
                 return 0;
             }
-            udelay(1000);
+            gdth_delay(1);
         }
-        prot_ver = (unchar)dp6m_ptr->u.ic.S_Info[0];
-        dp6m_ptr->u.ic.S_Status = 0;
+        prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]);
+        writeb(0, &dp6m_ptr->u.ic.S_Status);
         if (prot_ver != PROTOCOL_VERSION) {
             printk("GDT-PCI: Illegal protocol version\n");
-#if LINUX_VERSION_CODE >= 0x010300
-            if (remapped)
-                vfree((void *)ha->brd);
-#endif
+	    gdth_munmap(ha->brd);
             return 0;
         }
 
@@ -790,26 +985,23 @@
         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;
+        writel(0x00, &dp6m_ptr->u.ic.S_Info[0]);
+        writel(0x00, &dp6m_ptr->u.ic.S_Info[1]);
+        writel(0x01, &dp6m_ptr->u.ic.S_Info[2]);
+        writel(0x00, &dp6m_ptr->u.ic.S_Info[3]);
+        writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx);
+        writeb(1, &dp6m_ptr->i960r.ldoor_reg);
         retries = INIT_RETRIES;
-        JIFFYWAIT(2);
-        while (dp6m_ptr->u.ic.S_Status != 0xfe) {
+        gdth_delay(20);
+        while (readb(&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
+		gdth_munmap(ha->brd);
                 return 0;
             }
-            udelay(1000);
+            gdth_delay(1);
         }
-        dp6m_ptr->u.ic.S_Status = 0;
+        writeb(0, &dp6m_ptr->u.ic.S_Status);
     }
 
     return 1;
@@ -818,11 +1010,10 @@
 
 /* controller protocol functions */
 
-static void gdth_enable_int(int hanum)
+__initfunc (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;
@@ -834,27 +1025,27 @@
     cli();
 
     if (ha->type == GDT_EISA) {
-        addr = (ushort)ha->brd;
-        outb(0xff,addr+EDOORREG);
-        outb(0xff,addr+EDENABREG);
-        outb(0x01,addr+EINTENABREG);
+        outb(0xff, ha->bmic + EDOORREG);
+        outb(0xff, ha->bmic + EDENABREG);
+        outb(0x01, ha->bmic + 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;
+        writeb(1, &dp2_ptr->io.irqdel);
+        writeb(0, &dp2_ptr->u.ic.Cmd_Index);
+        writeb(1, &dp2_ptr->io.irqen);
     } 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;
+        writeb(1, &dp6_ptr->io.irqdel);
+        writeb(0, &dp6_ptr->u.ic.Cmd_Index);
+        writeb(1, &dp6_ptr->io.irqen);
     } else if (ha->type == GDT_PCINEW) {
-        outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
-        outb(0x03,PTR2USHORT(&ha->plx->control1));
+        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;
+        writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+        writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4,
+	       &dp6m_ptr->i960r.edoor_en_reg);
     }
     restore_flags(flags);
 }
@@ -874,15 +1065,15 @@
         if (ha->irq != (unchar)irq)             /* check IRQ */
             continue;
         if (ha->type == GDT_EISA)
-            *pIStatus = inb((ushort)ha->brd+EDOORREG);
+            *pIStatus = inb((ushort)ha->bmic + EDOORREG);
         else if (ha->type == GDT_ISA)
-            *pIStatus = ((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index;
+            *pIStatus = readb(&((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;
+            *pIStatus = readb(&((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;
+            *pIStatus = readb(&((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg);
    
         if (*pIStatus)                                  
             return i;                           /* board found */
@@ -900,15 +1091,15 @@
     
     ha = HADATA(gdth_ctr_tab[hanum]);
     if (ha->type == GDT_EISA)
-        gdtsema0 = (int)inb((ushort)ha->brd+SEMA0REG);
+        gdtsema0 = (int)inb(ha->bmic + SEMA0REG);
     else if (ha->type == GDT_ISA)
-        gdtsema0 = (int)((gdt2_dpram_str *)ha->brd)->u.ic.Sema0;
+        gdtsema0 = (int)readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
     else if (ha->type == GDT_PCI)
-        gdtsema0 = (int)((gdt6_dpram_str *)ha->brd)->u.ic.Sema0;
+        gdtsema0 = (int)readb(&((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;
+        gdtsema0 = (int)readb(&((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
 
     return (gdtsema0 & 1);
 }
@@ -942,15 +1133,15 @@
 
     ha = HADATA(gdth_ctr_tab[hanum]);
     if (ha->type == GDT_EISA)
-        outb(1,(ushort)ha->brd+SEMA0REG);
+        outb(1, ha->bmic + SEMA0REG);
     else if (ha->type == GDT_ISA)
-        ((gdt2_dpram_str *)ha->brd)->u.ic.Sema0 = 1;
+        writeb(1, &((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
     else if (ha->type == GDT_PCI)
-        ((gdt6_dpram_str *)ha->brd)->u.ic.Sema0 = 1;
+        writeb(1, &((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
     else if (ha->type == GDT_PCINEW)  
-        outb(1,PTR2USHORT(&ha->plx->sema0_reg));
+        outb(1, PTR2USHORT(&ha->plx->sema0_reg));
     else if (ha->type == GDT_PCIMPR)
-        ((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg = 1;
+        writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg);
     
 }
 
@@ -986,32 +1177,32 @@
     /* 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);
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
+        writew((ushort)cmd_ptr->Service, 
+	       &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id);
+	memcpy_toio(&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);
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp6_ptr->u.ic.comm_queue[cmd_no].offset);
+        writew((ushort)cmd_ptr->Service, 
+	       &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id);
+        memcpy_toio(&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);
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
+	writew((ushort)cmd_ptr->Service, 
+	       &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id);
+	memcpy_toio(&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);
+        writew(dp_offset + DPMEM_COMMAND_OFFSET, 
+	       &dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
+        writew((ushort)cmd_ptr->Service, 
+	       &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id);
+        memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count);
     }
 }
 
@@ -1039,17 +1230,17 @@
         ha->pccb->Service |= 0x80;
 
     if (ha->type == GDT_EISA) {
-        outb(ha->pccb->Service,(ushort)ha->brd+LDOORREG);
+        outb(ha->pccb->Service, ha->bmic + LDOORREG);
         if (ha->pccb->OpCode == GDT_INIT)               /* store DMA buffer */
-            outl((ulong)ha->pccb,(ushort)ha->brd+MAILBOXREG);
+            outl((ulong)ha->pccb, ha->bmic + MAILBOXREG);
     } else if (ha->type == GDT_ISA)
-        ((gdt2_dpram_str *)ha->brd)->io.event = 0;
+        writeb(0, &((gdt2_dpram_str *)ha->brd)->io.event);
     else if (ha->type == GDT_PCI)
-        ((gdt6_dpram_str *)ha->brd)->io.event = 0;
+        writeb(0, &((gdt6_dpram_str *)ha->brd)->io.event);
     else if (ha->type == GDT_PCINEW) 
-        outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+        outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
     else if (ha->type == GDT_PCIMPR)
-        ((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg = 1;
+        writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg);
 }
 
     
@@ -1075,12 +1266,12 @@
             answer_found = TRUE;
             break;
         }
-        udelay(1000);
+        gdth_delay(1);
     } while (--time);
     gdth_from_wait = FALSE;
     
     while (gdth_test_busy(hanum))
-        udelay(1);
+        gdth_delay(0);
 
     return (answer_found);
 }
@@ -1115,7 +1306,7 @@
                 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;
+                cmd_ptr->u.ioctl.p_param = virt_to_bus(ha->pscratch);
             } else {
                 cmd_ptr->u.cache.DeviceNo = (ushort)p1;
                 cmd_ptr->u.cache.BlockNo  = p2;
@@ -1131,14 +1322,14 @@
         ha->cmd_cnt          = 0;
         gdth_copy_command(hanum);
         gdth_release_event(hanum);
-        JIFFYWAIT(2);
+        gdth_delay(20);
         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);   
+        gdth_delay(1);   
     }   
     
     return (ha->status != S_OK ? 0:1);
@@ -1147,7 +1338,7 @@
 
 /* search for devices */
 
-static int gdth_search_drives(int hanum,int firsttime)
+__initfunc (static int gdth_search_drives(int hanum))
 {
     register gdth_ha_str *ha;
     ushort cdev_cnt,i;
@@ -1155,8 +1346,9 @@
     ulong drv_cyls, drv_hds, drv_secs;
     ulong bus_no;
     gdth_getch_str *chn;
+    gdth_iochan_str *ioc;
     
-    TRACE(("gdth_search_drives() hanum %d flag %d\n",hanum,firsttime));
+    TRACE(("gdth_search_drives() hanum %d\n",hanum));
     ha = HADATA(gdth_ctr_tab[hanum]);
 
     /* initialize controller services, at first: screen service */
@@ -1176,57 +1368,77 @@
     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"));
+    /* mount all cache devices */
+    gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,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;
+    /* 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));
+    /* detect number of SCSI buses - try new IOCTL */
+    ioc = (gdth_iochan_str *)DMADATA(gdth_ctr_tab[hanum]);
+    ioc->version 	= -1UL;
+    ioc->list_entries	= MAXBUS;
+    ioc->first_chan	= 0;
+    ioc->last_chan	= MAXBUS-1;
+    ioc->list_offset	= GDTOFFSOF(gdth_iochan_str, list[0]);
+    if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,GET_IOCHAN_DESC,
+			  INVALID_CHANNEL,sizeof(gdth_iochan_str))) {
+	TRACE2(("GET_IOCHAN_DESC supported!\n"));
+	ha->bus_cnt = ioc->chan_count;
+	for (bus_no = 0; bus_no < ha->bus_cnt; ++bus_no)
+	    if (ioc->list[bus_no].proc_id < MAXID)
+		ha->id[bus_no][ioc->list[bus_no].proc_id].type = SIOP_DTYP;
+    } else {
+	/* old method */
+	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));
+
+    /* read board info, fill ctr_name[] */
+    if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_INFO,
+			  INVALID_CHANNEL,sizeof(gdth_binfo_str))) {
+	TRACE2(("BOARD_INFO supported!\n"));
+	strcpy(ha->ctr_name, ((gdth_binfo_str *)DMADATA(gdth_ctr_tab[hanum]))->type_string);
+    } else {
+	strcpy(ha->ctr_name, gdth_ctr_name(hanum));
     }
+    TRACE2(("Controller name: %s\n",ha->ctr_name));
 
     /* initialize raw service */
     if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) {
@@ -1241,8 +1453,7 @@
     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))
-        {
+        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;
@@ -1260,9 +1471,20 @@
         }
     }
 
-    /* if it is not the first scan, we are ready */
-    if (!firsttime)
-        return 1;
+    /* reserve drives for raw service */
+    for (i = 0; reserve_list[i].hanum != 0xff; ++i) {
+	if (reserve_list[i].hanum < MAXHA && reserve_list[i].hanum == hanum &&
+	    reserve_list[i].bus < MAXBUS && reserve_list[i].id < MAXID) {
+	    TRACE2(("gdth_search_drives(): reserve ha %d bus %d id %d\n",
+		    reserve_list[i].hanum, reserve_list[i].bus,
+		    reserve_list[i].id));
+	    if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_RESERVE,0,
+				   reserve_list[i].bus, reserve_list[i].id)) {
+		printk("GDT: Error raw service (RESERVE, code %d)\n",
+		       ha->status);
+	    }
+	}
+    }
 
     /* scanning for raw devices */
     for (b=0; b<ha->bus_cnt; ++b) {
@@ -1301,27 +1523,33 @@
             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].size &= ~SECS32;
+	    if (ha->info2 == 0) {
+		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;
+		    }
+		}
+            } else {
+		drv_hds = ha->info2 & 0xff;
+		drv_secs = (ha->info2 >> 8) & 0xff;
+		drv_cyls = ha->id[b][t].size /drv_hds/drv_secs;
+	    }
             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",
+            TRACE2(("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 */
@@ -1355,8 +1583,8 @@
 
     ha = HADATA(gdth_ctr_tab[hanum]);
     scp->SCp.this_residual = (int)priority;
-    gdth_update_timeout(scp, scp->timeout * 6);
-#if LINUX_VERSION_CODE >= 0x010400
+    gdth_update_timeout(hanum, scp, scp->timeout_per_command * 6);
+#if LINUX_VERSION_CODE >= 0x020000
     b = scp->channel;
 #else
     b = NUMDATA(nscp->host)->busnum;
@@ -1365,7 +1593,7 @@
 #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);
+        scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0);
     }
 #endif
 
@@ -1418,7 +1646,7 @@
     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
+#if LINUX_VERSION_CODE >= 0x020000
         b = nscp->channel;
 #else
         b = NUMDATA(nscp->host)->busnum;
@@ -1434,7 +1662,7 @@
                         return;
                     }
                     while (gdth_test_busy(hanum))
-                        udelay(1000);
+                        gdth_delay(1);
                 }
                 firsttime = FALSE;
             }
@@ -1472,7 +1700,10 @@
                         TRACE2(("Prevent r. nonremov. drive->do nothing\n"));
                         nscp->result = DID_OK << 16;
                         restore_flags( flags );
-                        nscp->scsi_done(nscp);
+                        if (!nscp->SCp.have_data_in)
+                            nscp->SCp.have_data_in++;
+                        else
+                            nscp->scsi_done(nscp);
                         save_flags( flags );
                         cli();
                     } else {
@@ -1500,7 +1731,10 @@
                        nscp->cmnd[0]);
                     nscp->result = DID_ABORT << 16;
                     restore_flags( flags );
-                    nscp->scsi_done( nscp );
+                    if (!nscp->SCp.have_data_in)
+                        nscp->SCp.have_data_in++;
+                    else
+                        nscp->scsi_done(nscp);
                     save_flags( flags );
                     cli();
                     break;
@@ -1638,7 +1872,10 @@
     }
 
     restore_flags(*flags);
-    scp->scsi_done(scp);
+    if (!scp->SCp.have_data_in)
+        scp->SCp.have_data_in++;
+    else
+        scp->scsi_done(scp);
     save_flags(*flags);
     cli();
     return 1;
@@ -1709,7 +1946,7 @@
             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_ptr = virt_to_bus(sl->address);
                 cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length;
             }
             cmdp->u.cache.sg_canz = (ulong)i;
@@ -1726,11 +1963,11 @@
             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_ptr = virt_to_bus(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.DestAddr  = virt_to_bus(scp->request_buffer);
                 cmdp->u.cache.sg_canz= 0;
             }
         }
@@ -1806,7 +2043,7 @@
     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.sense_data = virt_to_bus(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);
@@ -1815,7 +2052,7 @@
         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_ptr = virt_to_bus(sl->address);
             cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length;
         }
         cmdp->u.raw.sg_ranz = (ulong)i;
@@ -1832,11 +2069,11 @@
         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_ptr = virt_to_bus(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.sdata  = virt_to_bus(scp->request_buffer);
             cmdp->u.raw.sg_ranz= 0;
         }
     }
@@ -2047,6 +2284,17 @@
 
 /* SCSI interface functions */
 
+#if LINUX_VERSION_CODE >= 0x02015F
+static void do_gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+{
+    ulong flags;
+
+    spin_lock_irqsave(&io_request_lock, flags);
+    gdth_interrupt(irq, dev_id, regs);
+    spin_unlock_irqrestore(&io_request_lock, flags);
+}
+#endif
+
 #if LINUX_VERSION_CODE >= 0x010346
 static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
 #else
@@ -2091,49 +2339,55 @@
     if (ha->type == GDT_EISA) {
         if (IStatus & 0x80) {                   /* error flag */
             IStatus &= ~0x80;
-            CmdStatus = inw((ushort)ha->brd+MAILBOXREG+8);
+            CmdStatus = inw(ha->bmic + 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);
+                Service = inw(ha->bmic + MAILBOXREG+10);
+                InfoBytes2 = inl(ha->bmic + 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 */
+        InfoBytes = inl(ha->bmic + MAILBOXREG+12);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+        outb(0xff, ha->bmic + EDOORREG);    /* acknowledge interrupt */
+        outb(0x00, ha->bmic + 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;
+            CmdStatus = readw(&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];
+                Service = readw(&dp2_ptr->u.ic.Service);
+                InfoBytes2 = readl(&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 */
+        InfoBytes = readl(&dp2_ptr->u.ic.Info[0]);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+        writeb(0xff, &dp2_ptr->io.irqdel);              /* acknowledge interrupt */
+        writeb(0, &dp2_ptr->u.ic.Cmd_Index);            /* reset command index */
+        writeb(0, &dp2_ptr->io.Sema1);                 /* 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;
+            CmdStatus = readw(&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];
+                Service = readw(&dp6_ptr->u.ic.Service);
+                InfoBytes2 = readl(&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 */
+        InfoBytes = readl(&dp6_ptr->u.ic.Info[0]);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+        writeb(0xff, &dp6_ptr->io.irqdel);              /* acknowledge interrupt */
+        writeb(0, &dp6_ptr->u.ic.Cmd_Index);            /* reset command index */
+        writeb(0, &dp6_ptr->io.Sema1);                 /* reset status semaphore */
     } else if (ha->type == GDT_PCINEW) {
         if (IStatus & 0x80) {                   /* error flag */
             IStatus &= ~0x80;
@@ -2147,23 +2401,27 @@
             CmdStatus = S_OK;
 
         InfoBytes = inl(PTR2USHORT(&ha->plx->info[0]));
-        outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); 
-        outb(0x00,PTR2USHORT(&ha->plx->sema1_reg)); 
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+        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;
+            CmdStatus = readw(&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];
+                Service = readw(&dp6m_ptr->i960r.service);
+                InfoBytes2 = readl(&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;
+        InfoBytes = readl(&dp6m_ptr->i960r.info[0]);
+	if (gdth_polling)			/* init. -> more info */
+	    InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+        writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+        writeb(0, &dp6m_ptr->i960r.sema1_reg);
     } else {
         TRACE2(("gdth_interrupt() unknown controller type\n"));
         return;
@@ -2238,7 +2496,7 @@
 
         if (msg->msg_ext && !msg->msg_answer) {
             while (gdth_test_busy(hanum))
-                udelay(1);
+                gdth_delay(0);
             cmdp->Service       = SCREENSERVICE;
             cmdp->RequestBuffer = SCREEN_CMND;
             gdth_get_cmd_index(hanum);
@@ -2275,7 +2533,7 @@
             }
             msg->msg_len = i;
             while (gdth_test_busy(hanum))
-                udelay(1);
+                gdth_delay(0);
             cmdp->Service       = SCREENSERVICE;
             cmdp->RequestBuffer = SCREEN_CMND;
             gdth_get_cmd_index(hanum);
@@ -2302,7 +2560,7 @@
             scp->result = DID_OK << 16;
         } else if (ha->status == S_BSY) {
             TRACE2(("Controller busy -> retry !\n"));
-            gdth_putq(hanum,scp,DEFAULT_PRI);
+            gdth_putq(hanum,scp,scp->SCp.this_residual);
             return 1;
         } else {
             if (service == CACHESERVICE) {
@@ -2318,7 +2576,7 @@
                     dvr.eu.sync.status  = ha->status;
                     dvr.eu.sync.info    = ha->info;
                     dvr.eu.sync.hostdrive =
-#if LINUX_VERSION_CODE >= 0x010400
+#if LINUX_VERSION_CODE >= 0x020000
                         ha->id[scp->channel][scp->target].hostdrive;
 #else
                         ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive;
@@ -2329,15 +2587,17 @@
                         gdth_store_event(ES_SYNC, service, &dvr);
                 }
             } else {
-                if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) {
+                if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL || ha->info>=0x100) {
                     scp->result = DID_BAD_TARGET << 16;
                 } else {
                     scp->result = (DID_OK << 16) | ha->info;
                 }
             }
         }
-        scp->SCp.have_data_in++;
-        scp->scsi_done(scp);
+	if (!scp->SCp.have_data_in)
+	    scp->SCp.have_data_in++;
+	else
+	    scp->scsi_done(scp);
     }
 
     return 1;
@@ -2456,6 +2716,12 @@
         "GDT HA %u, CPU temperature critical",
 /*55*/  "\003\000\002"
         "GDT HA %u, CPU temperature OK",
+/*56*/  "\005\000\002\006\004"
+        "GDT HA %u, Host drive %lu created",
+/*57*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand restarted",
+/*58*/  "\005\000\002\006\002"
+        "GDT HA %u, Array Drive %u: expand stopped",
 };
 
 
@@ -2479,7 +2745,7 @@
     if (service == SCREENSERVICE) {
         if (ha->status == MSG_REQUEST) {
             while (gdth_test_busy(hanum))
-                udelay(1);
+                gdth_delay(0);
             cmdp->Service       = SCREENSERVICE;
             cmdp->RequestBuffer = SCREEN_CMND;
             cmd_index = gdth_get_cmd_index(hanum);
@@ -2546,8 +2812,9 @@
     return 1;
 }
 
+
 #ifdef GDTH_STATISTICS
-void gdth_timeout(void)
+void gdth_timeout(ulong data)
 {
     ulong flags,i;
     Scsi_Cmnd *nscp;
@@ -2569,13 +2836,14 @@
             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();
+    gdth_timer.expires = jiffies + 30 * HZ;
+    add_timer(&gdth_timer);
+    restore_flags(flags);
 }
 #endif
 
-int gdth_detect(Scsi_Host_Template *shtp)
+
+__initfunc (int gdth_detect(Scsi_Host_Template *shtp))
 {
     struct Scsi_Host *shp;
     gdth_ha_str *ha;
@@ -2600,7 +2868,7 @@
 #else
     printk("Console\n");
 #endif
-    WAITSEC(3);
+    gdth_delay(3000);
 #endif
 
     TRACE(("gdth_detect()\n"));
@@ -2625,7 +2893,7 @@
         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)) {
+            if (!gdth_init_isa(isa_bios,ha)) {
                 scsi_unregister(shp);
                 continue;
             }
@@ -2635,7 +2903,9 @@
 
             save_flags(flags);
             cli();
-#if LINUX_VERSION_CODE >= 0x010346 
+#if LINUX_VERSION_CODE >= 0x02015F 
+            if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#elif 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")) 
@@ -2682,13 +2952,15 @@
                 for (j=0; j<MAXID; ++j) {
                     ha->id[i][j].type = EMPTY_DTYP;
                     ha->id[i][j].lock = 0;
+		    ha->id[i][j].heads = 0;
                 }
             }
             restore_flags(flags);
 
-            if (!gdth_search_drives(hanum,TRUE)) {
+            if (!gdth_search_drives(hanum)) {
                 printk("GDT-ISA: Error during device scan\n");
                 --gdth_ctr_count;
+		--gdth_ctr_vcount;
                 save_flags(flags);
                 cli();
 #if LINUX_VERSION_CODE >= 0x010346 
@@ -2701,9 +2973,9 @@
                 continue;
             }
 
-#if LINUX_VERSION_CODE >= 0x010400
+#if LINUX_VERSION_CODE >= 0x020000
             shp->max_id      = 8;
-            shp->max_lun     = 8;
+            shp->max_lun     = MAXLUN;
             shp->max_channel = ha->bus_cnt - 1;
 #else
             /* register addit. SCSI channels as virtual controllers */
@@ -2733,7 +3005,7 @@
         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)) {
+            if (!gdth_init_eisa(eisa_slot,ha)) {
                 scsi_unregister(shp);
                 continue;
             }
@@ -2743,7 +3015,9 @@
 
             save_flags(flags);
             cli();
-#if LINUX_VERSION_CODE >= 0x010346 
+#if LINUX_VERSION_CODE >= 0x02015F 
+            if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#elif 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")) 
@@ -2779,13 +3053,15 @@
                 for (j=0; j<MAXID; ++j) {
                     ha->id[i][j].type = EMPTY_DTYP;
                     ha->id[i][j].lock = 0;
+		    ha->id[i][j].heads = 0;
                 }
             }
             restore_flags(flags);
 
-            if (!gdth_search_drives(hanum,TRUE)) {
+            if (!gdth_search_drives(hanum)) {
                 printk("GDT-EISA: Error during device scan\n");
                 --gdth_ctr_count;
+		--gdth_ctr_vcount;
                 save_flags(flags);
                 cli();
 #if LINUX_VERSION_CODE >= 0x010346 
@@ -2798,9 +3074,9 @@
                 continue;
             }
 
-#if LINUX_VERSION_CODE >= 0x010400
+#if LINUX_VERSION_CODE >= 0x020000
             shp->max_id      = 8;
-            shp->max_lun     = 8;
+            shp->max_lun     = MAXLUN;
             shp->max_channel = ha->bus_cnt - 1;
 #else
             /* register addit. SCSI channels as virtual controllers */
@@ -2829,7 +3105,7 @@
     }
 
     /* scanning for PCI controllers */
-    for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) {
+    for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDTMAXRP; ++device_id) {
         if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 &&
             device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP)
             continue;
@@ -2838,7 +3114,7 @@
                 break;                          /* next device_id */
             shp = scsi_register(shtp,sizeof(gdth_ext_str));
             ha = HADATA(shp);
-            if (!gdth_init_pci(&pcistr,ha,TRUE)) {
+            if (!gdth_init_pci(&pcistr,ha)) {
                 scsi_unregister(shp);
                 continue;
             }
@@ -2848,10 +3124,12 @@
 
             save_flags(flags);
             cli();
-#if LINUX_VERSION_CODE >= 0x010346 
-            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#if LINUX_VERSION_CODE >= 0x02015F 
+            if (request_irq(ha->irq,do_gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth",NULL))
+#elif LINUX_VERSION_CODE >= 0x010346 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth",NULL))
 #else
-            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) 
+            if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT|SA_SHIRQ,"gdth")) 
 #endif
             {
                 printk("GDT-PCI: Unable to allocate IRQ\n");
@@ -2882,13 +3160,15 @@
                 for (j=0; j<MAXID; ++j) {
                     ha->id[i][j].type = EMPTY_DTYP;
                     ha->id[i][j].lock = 0;
+		    ha->id[i][j].heads = 0;
                 }
             }
             restore_flags(flags);
 
-            if (!gdth_search_drives(hanum,TRUE)) {
+            if (!gdth_search_drives(hanum)) {
                 printk("GDT-PCI: Error during device scan\n");
                 --gdth_ctr_count;
+		--gdth_ctr_vcount;
                 save_flags(flags);
                 cli();
 #if LINUX_VERSION_CODE >= 0x010346 
@@ -2901,9 +3181,9 @@
                 continue;
             }
 
-#if LINUX_VERSION_CODE >= 0x010400
-            shp->max_id      = 8;
-            shp->max_lun     = 8;
+#if LINUX_VERSION_CODE >= 0x020000
+            shp->max_id      = MAXID;
+            shp->max_lun     = MAXLUN;
             shp->max_channel = ha->bus_cnt - 1;
 #else
             /* register addit. SCSI channels as virtual controllers */
@@ -2929,14 +3209,19 @@
     }
 
     TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count));
-
+    if (gdth_ctr_count > 0) {
 #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;
+	TRACE2(("gdth_detect(): Initializing timer !\n"));
+	init_timer(&gdth_timer);
+	gdth_timer.expires = jiffies + HZ;
+	gdth_timer.data = 0L;
+	gdth_timer.function = gdth_timeout;
+	add_timer(&gdth_timer);
 #endif
-
+#if LINUX_VERSION_CODE >= 0x020100
+	register_reboot_notifier(&gdth_notifier);
+#endif
+    }
     gdth_polling = FALSE;
     return gdth_ctr_vcount;
 }
@@ -2948,9 +3233,11 @@
 
     TRACE2(("gdth_release()\n"));
 
-    save_flags(flags);
-    cli();
     if (NUMDATA(shp)->busnum == 0) {
+	gdth_flush(NUMDATA(shp)->hanum);
+
+	save_flags(flags);
+	cli();
         if (shp->irq) {
 #if LINUX_VERSION_CODE >= 0x010346
             free_irq(shp->irq,NULL);
@@ -2961,9 +3248,21 @@
         if (shp->dma_channel != 0xff) {
             free_dma(shp->dma_channel);
         }
+	restore_flags(flags);
+	gdth_ctr_released++;
+	TRACE2(("gdth_release(): HA %d of %d\n", 
+		gdth_ctr_released, gdth_ctr_count));
+
+	if (gdth_ctr_released == gdth_ctr_count) {
+#ifdef GDTH_STATISTICS
+	    del_timer(&gdth_timer);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+	    unregister_reboot_notifier(&gdth_notifier);
+#endif
+	}
     }
 
-    restore_flags(flags);
     scsi_unregister(shp);
     return 0;
 }
@@ -2980,102 +3279,40 @@
     if (ha->type == GDT_EISA) {
         switch (ha->stype) {
           case GDT3_ID:
-            return("GDT3000/3020 (EISA)");
+            return("GDT3000/3020");
           case GDT3A_ID:
-            return("GDT3000A/3020A/3050A (EISA)");
+            return("GDT3000A/3020A/3050A");
           case GDT3B_ID:
-            return("GDT3000B/3010A (EISA)");
+            return("GDT3000B/3010A");
         }
     } else if (ha->type == GDT_ISA) {
-        return("GDT2000/2020 (ISA)");
+        return("GDT2000/2020");
     } else if (ha->type == GDT_PCI) {
         switch (ha->stype) {
           case PCI_DEVICE_ID_VORTEX_GDT60x0:
-            return("GDT6000/6020/6050 (PCI)");
+            return("GDT6000/6020/6050");
           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)");
+            return("GDT6000B/6010");
         }
-    } 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)");
-        }
-    }
+    } 
+    /* new controllers (GDT_PCINEW, GDT_PCIMPR, ..) use board_info IOCTL! */
+
     return("");
 }
 
 const char *gdth_info(struct Scsi_Host *shp)
 {
     int hanum;
-    
+    gdth_ha_str *ha;
+
     TRACE2(("gdth_info()\n"));
     hanum = NUMDATA(shp)->hanum;
+    ha    = HADATA(gdth_ctr_tab[hanum]);
 
-    return (gdth_ctr_name(hanum));
+    return ((const char *)ha->ctr_name);
 }
 
-
+/* old error handling */
 int gdth_abort(Scsi_Cmnd *scp)
 {
     TRACE2(("gdth_abort() reason %d\n",scp->abort_reason));
@@ -3092,6 +3329,32 @@
     return SCSI_RESET_PUNT;
 }
 
+#if LINUX_VERSION_CODE >= 0x02015F
+/* new error handling */
+int gdth_eh_abort(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_eh_abort()\n"));
+    return FAILED;
+}
+
+int gdth_eh_device_reset(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_eh_device_reset()\n"));
+    return FAILED;
+}
+
+int gdth_eh_bus_reset(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_eh_bus_reset()\n"));
+    return FAILED;
+}
+
+int gdth_eh_host_reset(Scsi_Cmnd *scp)
+{
+    TRACE2(("gdth_eh_host_reset()\n"));
+    return FAILED;
+}
+#endif
 
 #if LINUX_VERSION_CODE >= 0x010300
 int gdth_bios_param(Disk *disk,kdev_t dev,int *ip)
@@ -3099,23 +3362,38 @@
 int gdth_bios_param(Disk *disk,int dev,int *ip)
 #endif
 {
-    TRACE2(("gdth_bios_param()\n"));
+    unchar b, t;
+    int hanum;
+    gdth_ha_str *ha;
+    int drv_hds, drv_secs;
+
+    hanum = NUMDATA(disk->device->host)->hanum;
+    b = disk->device->channel;
+    t = disk->device->id;
+    TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    if (ha->id[b][t].heads == 0) {
+	/* raw device: evaluate mapping (sectors per head, heads per cylinder) */
+	if (disk->capacity /HEADS/SECS <= MAXCYLS) {
+	    drv_hds = HEADS;
+	    drv_secs= SECS;
+	} else if (disk->capacity /MEDHEADS/MEDSECS <= MAXCYLS) {
+	    drv_hds = MEDHEADS;
+	    drv_secs= MEDSECS;
+	} else {
+	    drv_hds = BIGHEADS;
+	    drv_secs= BIGSECS;
+	}
+	ha->id[b][t].heads = drv_hds;
+	ha->id[b][t].secs = drv_secs;
+	TRACE2(("gdth_bios_param(): raw device -> params evaluated\n"));
+    }
+
+    ip[0] = ha->id[b][t].heads;
+    ip[1] = ha->id[b][t].secs;
+    ip[2] = disk->capacity / ip[0] / ip[1];
 
-    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;
@@ -3149,7 +3427,7 @@
                   scp->cmnd[0],scp->target,scp->lun));
     
     scp->scsi_done = (void *)done;
-    scp->SCp.have_data_in = 0;
+    scp->SCp.have_data_in = 1;
     hanum = NUMDATA(scp->host)->hanum;
 #ifdef GDTH_STATISTICS
     ++act_ios;
@@ -3165,22 +3443,84 @@
     return 0;
 }
 
+/* flush routine */
+static void gdth_flush(int hanum)
+{
+    int             i, j;
+    gdth_ha_str     *ha;
+    Scsi_Cmnd       scp;
+    Scsi_Device     sdev;
+    gdth_cmd_str    gdtcmd;
+    char            cmnd[12];
+
+    TRACE2(("gdth_flush() hanum %d\n",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;
+
+    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_flush(): 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;
+		    scp.SCp.this_residual = IOCTL_PRI;
+		    scsi_do_cmd(&scp, cmnd, &gdtcmd,
+				sizeof(gdth_cmd_str), gdth_scsi_done,
+				30*HZ, 1);
+		    down(&sem);
+		}
+	    }
+	}
+    }
+}
 
 /* shutdown routine */
-void gdth_halt()
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf)
+#else
+void gdth_halt(void)
+#endif
 {
-    int             hanum, i, j;
-    gdth_ha_str     *ha;
+    int             hanum;
     Scsi_Cmnd       scp;
     Scsi_Device     sdev;
     gdth_cmd_str    gdtcmd;
     char            cmnd[12];
 
+#if LINUX_VERSION_CODE >= 0x020100
+    TRACE2(("gdth_halt() event %d\n",event));
+    if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
+	return NOTIFY_DONE;
+#else
     TRACE2(("gdth_halt()\n"));
+    if (halt_called) {
+	TRACE2(("already called\n"));
+	return;
+    }
+    halt_called = TRUE;
+#endif
     printk("GDT: Flushing all host drives .. ");
-
     for (hanum = 0; hanum < gdth_ctr_count; ++hanum) {
-        ha = HADATA(gdth_ctr_tab[hanum]);
+        gdth_flush(hanum);
+
+        /* controller reset */
         memset(&sdev,0,sizeof(Scsi_Device));
         memset(&scp, 0,sizeof(Scsi_Cmnd));
         sdev.host = gdth_ctr_tab[hanum];
@@ -3191,32 +3531,6 @@
         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;
@@ -3225,6 +3539,7 @@
             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, &gdtcmd,
                 sizeof(gdth_cmd_str), gdth_scsi_done,
                 10*HZ, 1);
@@ -3232,11 +3547,19 @@
         }
     }
     printk("Done.\n");
+
+#ifdef GDTH_STATISTICS
+    del_timer(&gdth_timer);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+    unregister_reboot_notifier(&gdth_notifier);
+    return NOTIFY_OK;
+#endif
 }
 
 
 /* called from init/main.c */
-void gdth_setup(char *str,int *ints)
+__initfunc (void gdth_setup(char *str,int *ints))
 {
     static size_t setup_idx = 0;
 
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth.h linux/drivers/scsi/gdth.h
--- v2.0.35/linux/drivers/scsi/gdth.h	Wed Oct 15 15:12:17 1997
+++ linux/drivers/scsi/gdth.h	Sun Nov 15 10:33:09 1998
@@ -4,13 +4,13 @@
 /*
  * 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
+ * gdth.h Copyright (C) 1995-98 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 $
+ * $Id: gdth.h,v 1.15 1998/09/28 15:50:10 achim Exp $
  */
 
 #include <linux/version.h>
@@ -29,9 +29,9 @@
 /* defines, macros */
 
 /* driver version */
-#define GDTH_VERSION_STR        "1.00"
+#define GDTH_VERSION_STR        "1.07"
 #define GDTH_VERSION            1
-#define GDTH_SUBVERSION         0
+#define GDTH_SUBVERSION         7
 
 /* protocol version */
 #define PROTOCOL_VERSION        1
@@ -101,15 +101,26 @@
 #define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125   /* GDT6121RP2/GDT6521RP2 */
 #endif
 
+#ifndef PCI_DEVICE_ID_VORTEX_GDT6519RD
+/* GDT_MPR, Fibre Channel */
+#define PCI_DEVICE_ID_VORTEX_GDT6519RD  0x210   /* GDT6519RD */
+#define PCI_DEVICE_ID_VORTEX_GDT6529RD  0x211   /* GDT6529RD */
+#endif
+
+#ifndef PCI_DEVICE_ID_VORTEX_GDTMAXRP
+/* GDT_MPR, last device ID */
+#define PCI_DEVICE_ID_VORTEX_GDTMAXRP  	0x2ff   
+#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 MAXID           16
 #define MAXLUN          8
-#define MAXBUS          5
+#define MAXBUS          6
 #define MAX_HDRIVES     35                      /* max. host drive count */
 #define MAX_EVENTS      100                     /* event buffer count */
 #define MAXCYLS         1024
@@ -147,6 +158,7 @@
 #define SECTOR_SIZE     0x200                   /* always 512 bytes per sector */
 
 /* DPMEM constants */
+#define DPMEM_MAGIC	0xC0FFEE11
 #define IC_HEADER_BYTES 48
 #define IC_QUEUE_BYTES  4
 #define DPMEM_COMMAND_OFFSET    IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
@@ -170,9 +182,11 @@
 
 /* IOCTL command defines */
 #define SCSI_CHAN_CNT   5                       /* subfunctions */
+#define GET_IOCHAN_DESC	0x5e
 #define L_CTRL_PATTERN  0x20000000L
 #define CACHE_INFO      4
 #define CACHE_CONFIG    5
+#define BOARD_INFO	0x28
 #define IO_CHANNEL      0x00020000L             /* channels */
 #define INVALID_CHANNEL 0x0000ffffL     
 
@@ -254,6 +268,21 @@
     unchar      siop_state;                     /* SCSI processor state */ 
 } gdth_getch_str;
 
+/* get raw channel count IOCTL (NEW!) */
+typedef struct {
+    ulong	version;			/* version of information (-1UL: newest) */
+    unchar	list_entries;			/* list entry count */
+    unchar	first_chan;			/* first channel number */
+    unchar	last_chan;			/* last channel number */
+    unchar	chan_count;			/* (R) channel count */
+    ulong	list_offset;			/* offset of list[0] */
+    struct {
+	unchar	proc_id;			/* processor id */
+	unchar	proc_defect;			/* defect ? */
+	unchar	reserved[2];
+    } list[MAXBUS];
+} gdth_iochan_str;
+
 /* cache info/config IOCTL */
 typedef struct {
     ulong       version;                        /* firmware version */
@@ -277,6 +306,35 @@
     gdth_cstat_str  cstat;
 } gdth_cinfo_str;
 
+/* board info IOCTL */
+typedef struct {
+    ulong	ser_no;				/* serial no. */
+    unchar	oem_id[2];			/* OEM ID */
+    ushort	ep_flags;			/* eprom flags */
+    ulong	proc_id;			/* processor ID */
+    ulong	memsize;			/* memory size (bytes) */
+    unchar	mem_banks;			/* memory banks */
+    unchar	chan_type;			/* channel type */
+    unchar	chan_count;			/* channel count */
+    unchar	rdongle_pres;			/* dongle present? */
+    ulong	epr_fw_ver;			/* (eprom) firmware version */
+    ulong	upd_fw_ver;			/* (update) firmware version */
+    ulong	upd_revision;			/* update revision */
+    char	type_string[16];		/* controller name */
+    char	raid_string[16];		/* RAID firmware name */
+    unchar	update_pres;			/* update present? */
+    unchar	xor_pres;			/* XOR engine present? */
+    unchar	prom_type;			/* ROM type (eprom/flash eprom) */
+    unchar	prom_count;			/* number of ROM devices */
+    ulong	dup_pres;			/* duplexing module present? */
+    ulong	chan_pres;			/* number of expansion channels */
+    ulong	mem_pres;			/* memory expansion installed? */
+    unchar	ft_bus_system;			/* fault bus supported? */
+    unchar	subtype_valid;			/* board_subtype valid? */
+    unchar	board_subtype;			/* controller subtype/hardware level */
+    unchar	ramparity_pres;			/* RAM parity check hardware present? */
+} gdth_binfo_str; 
+
 /* scatter/gather element */
 typedef struct {
     ulong       sg_ptr;                         /* address */
@@ -550,9 +608,10 @@
     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 */
+    ushort              cache_feat;             /* feat. cache serv. (s/g,..) */
+    ushort		bmic;			/* BMIC address (EISA) */
+    void               	*brd;	                /* 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 */
@@ -580,6 +639,7 @@
     unchar              mode;                   /* information from /proc */
     ushort              param_size;
     gdth_cpar_str       cpar;                   /* controller cache par. */
+    char		ctr_name[16];		/* controller name */
 } gdth_ha_str;
 
 /* structure for scsi_register(), SCSI bus != 0 */
@@ -648,12 +708,22 @@
     } bd;
 } gdth_modep_data;
 
+/* stack frame */
 typedef struct {
     ulong       b[10];                          /* 32 bit compiler ! */
 } gdth_stackframe;
 
 #pragma pack()
 
+
+/* data structure for reserve drives */
+typedef struct {
+    unchar	hanum;				
+    unchar	bus;
+    unchar	id;
+} gdth_reserve_str;
+
+
 /* function prototyping */
 
 int gdth_detect(Scsi_Host_Template *);
@@ -668,8 +738,38 @@
 #endif
 const char *gdth_info(struct Scsi_Host *);
 
-
-#if LINUX_VERSION_CODE >= 0x010300
+#if LINUX_VERSION_CODE >= 0x02015F
+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);
+int gdth_eh_abort(Scsi_Cmnd *scp);
+int gdth_eh_device_reset(Scsi_Cmnd *scp);
+int gdth_eh_bus_reset(Scsi_Cmnd *scp);
+int gdth_eh_host_reset(Scsi_Cmnd *scp);
+#define GDTH { proc_dir:        &proc_scsi_gdth,                 \
+               proc_info:       gdth_proc_info,                  \
+               name:            "GDT SCSI Disk Array Controller",\
+               detect:          gdth_detect,                     \
+               release:         gdth_release,                    \
+               info:            gdth_info,                       \
+               command:         gdth_command,                    \
+               queuecommand:    gdth_queuecommand,               \
+               eh_abort_handler: gdth_eh_abort,                  \
+               eh_device_reset_handler: gdth_eh_device_reset,    \
+               eh_bus_reset_handler: gdth_eh_bus_reset,          \
+               eh_host_reset_handler: gdth_eh_host_reset,        \
+               abort:           gdth_abort,                      \
+               reset:           gdth_reset,                      \
+               bios_param:      gdth_bios_param,                 \
+               can_queue:       GDTH_MAXCMDS,                    \
+               this_id:         -1,                              \
+               sg_tablesize:    GDTH_MAXSG,                      \
+               cmd_per_lun:     GDTH_MAXC_P_L,                   \
+               present:         0,                               \
+               unchecked_isa_dma: 1,                             \
+               use_clustering:  ENABLE_CLUSTERING,               \
+               use_new_eh_code: 1       /* use new error code */ }    
+#elif 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);
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth_proc.c linux/drivers/scsi/gdth_proc.c
--- v2.0.35/linux/drivers/scsi/gdth_proc.c	Mon Aug  4 17:15:07 1997
+++ linux/drivers/scsi/gdth_proc.c	Sun Nov 15 10:33:09 1998
@@ -1,5 +1,5 @@
 /* gdth_proc.c 
- * $Id: gdth_proc.c,v 1.4 1997/02/25 13:33:47 achim Exp $
+ * $Id: gdth_proc.c,v 1.10 1998/06/03 14:53:49 achim Exp $
  */
 
 #include "gdth_ioctl.h"
@@ -10,7 +10,7 @@
     int hanum,busnum,i;
 
     TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n",
-            length,hostno,offset,inout));
+            length,hostno,(int)offset,inout));
 
     for (i=0; i<gdth_ctr_vcount; ++i) {
         if (gdth_ctr_vtab[i]->host_no == hostno)
@@ -113,6 +113,7 @@
                         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, &gdtcmd,
                                     sizeof(gdth_cmd_str), gdth_scsi_done,
                                     30*HZ, 1);
@@ -170,7 +171,7 @@
         gdtcmd.BoardNode = LOCALBOARD;
         gdtcmd.Service = CACHESERVICE;
         gdtcmd.OpCode = GDT_IOCTL;
-        gdtcmd.u.ioctl.p_param = (ulong)pcpar;
+        gdtcmd.u.ioctl.p_param = virt_to_bus(pcpar);
         gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str);
         gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
         gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
@@ -179,6 +180,7 @@
             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, &gdtcmd, sizeof(gdth_cmd_str),
                         gdth_scsi_done, 30*HZ, 1);
             down(&sem);
@@ -257,7 +259,7 @@
         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;
+            *ppadd = virt_to_bus(piord->iu.general.data);
         }
         /* do IOCTL */
         {
@@ -291,7 +293,7 @@
         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);
+            piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 0x10);
         } else if (ha->type != GDT_PCIMPR) {
             piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6);
         } else {
@@ -433,14 +435,14 @@
 
     /* look for buffer ID in length */
     if (id > 4) {
-#if LINUX_VERSION_CODE >= 0x010400
+#if LINUX_VERSION_CODE >= 0x020000
         size = sprintf(buffer+len,
                        "%s SCSI Disk Array Controller\n",
-                       gdth_ctr_name(hanum));
+                       ha->ctr_name);
 #else
         size = sprintf(buffer+len,
                        "%s SCSI Disk Array Controller (SCSI Bus %d)\n",
-                       gdth_ctr_name(hanum),busnum);
+                       ha->ctr_name,busnum);
 #endif
         len += size;  pos = begin + len;
         size = sprintf(buffer+len,
@@ -479,7 +481,7 @@
     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));
+            len,(int)pos,(int)begin,(int)offset,length,size));
     return(len);
 }
 
@@ -542,16 +544,19 @@
 
     for (i = 0; i < GDTH_MAXCMDS; ++i) {
         scp = gdth_cmd_tab[i][hanum].cmnd;
+#if LINUX_VERSION_CODE >= 0x020000
         if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
-#if LINUX_VERSION_CODE >= 0x010400
             scp->channel == (unchar)busnum)
 #else
+        if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
             NUMDATA(scp->host)->busnum == (unchar)busnum)
 #endif
         {
+	    scp->SCp.have_data_in = 0;
             restore_flags(flags);
             while (!scp->SCp.have_data_in)
                 barrier();
+	    scp->scsi_done(scp);
             save_flags(flags);
             cli();
         }
@@ -570,15 +575,16 @@
     ha = HADATA(gdth_ctr_tab[hanum]);
 
     for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+#if LINUX_VERSION_CODE >= 0x020000
         if (scp->target == (unchar)id &&
-#if LINUX_VERSION_CODE >= 0x010400
             scp->channel == (unchar)busnum)
 #else
+        if (scp->target == (unchar)id &&
             NUMDATA(scp->host)->busnum == (unchar)busnum)
 #endif
         {
             TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
-            scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+            scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0);
         }
     }
     restore_flags(flags);
@@ -595,30 +601,44 @@
     ha = HADATA(gdth_ctr_tab[hanum]);
 
     for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+#if LINUX_VERSION_CODE >= 0x020000
         if (scp->target == (unchar)id &&
-#if LINUX_VERSION_CODE >= 0x010400
             scp->channel == (unchar)busnum)
 #else
+        if (scp->target == (unchar)id &&
             NUMDATA(scp->host)->busnum == (unchar)busnum)
 #endif
         {
             TRACE2(("gdth_start_timeout(): update_timeout()\n"));
-            gdth_update_timeout(scp, scp->SCp.buffers_residual);
+            gdth_update_timeout(hanum, scp, scp->SCp.buffers_residual);
         }
     }
     restore_flags(flags);
 }
 
-static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout)
+static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout)
 {
     ulong flags;
     int oldto;
 
     save_flags(flags);
     cli();
+    oldto = scp->timeout_per_command;
+    scp->timeout_per_command = timeout;
 
-    oldto = scp->timeout;
-    scp->timeout = timeout;
+#if LINUX_VERSION_CODE >= 0x02014B
+    if (timeout == 0) {
+	del_timer(&scp->eh_timeout);
+	scp->eh_timeout.data = (unsigned long) NULL;
+	scp->eh_timeout.expires = 0;
+    } else {
+	if (scp->eh_timeout.data != (unsigned long) NULL) 
+	    del_timer(&scp->eh_timeout);
+	scp->eh_timeout.data = (unsigned long) scp;
+	scp->eh_timeout.expires = jiffies + timeout;
+	add_timer(&scp->eh_timeout);
+    }
+#else
     if (timeout > 0) {
         if (timer_table[SCSI_TIMER].expires == 0) {
             timer_table[SCSI_TIMER].expires = jiffies + timeout;
@@ -628,6 +648,7 @@
                 timer_table[SCSI_TIMER].expires = jiffies + timeout;
         }
     }
+#endif
 
     restore_flags(flags);
     return oldto;
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/gdth_proc.h linux/drivers/scsi/gdth_proc.h
--- v2.0.35/linux/drivers/scsi/gdth_proc.h	Mon Aug  4 17:15:07 1997
+++ linux/drivers/scsi/gdth_proc.h	Sun Nov 15 10:33:09 1998
@@ -2,7 +2,7 @@
 #define _GDTH_PROC_H
 
 /* gdth_proc.h 
- * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $
+ * $Id: gdth_proc.h,v 1.4 1998/06/10 12:20:34 achim Exp $
  */
 
 static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
@@ -16,7 +16,7 @@
 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);
+static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout);
 
 void gdth_scsi_done(Scsi_Cmnd *scp);
 
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.0.35/linux/drivers/scsi/hosts.c	Sun Nov 15 10:49:44 1998
+++ linux/drivers/scsi/hosts.c	Sun Nov 15 10:33:09 1998
@@ -161,6 +161,10 @@
 #include "AM53C974.h"
 #endif
 
+#ifdef CONFIG_SCSI_MEGARAID
+#include "megaraid.h"
+#endif
+
 #ifdef CONFIG_SCSI_PPA
 #include "ppa.h"
 #endif
@@ -312,6 +316,9 @@
 #ifdef CONFIG_SCSI_AM53C974
     AM53C974,
 #endif
+#ifdef CONFIG_SCSI_MEGARAID
+    MEGARAID,
+#endif
 #ifdef CONFIG_SCSI_PPA
     PPA,
 #endif
@@ -357,10 +364,18 @@
     
     /* If we are removing the last host registered, it is safe to reuse
      * its host number (this avoids "holes" at boot time) (DB) 
+     * It is also safe to reuse those of numbers directly below which have
+     * been released earlier (to avoid some holes in numbering).
      */
-    if (max_scsi_hosts == next_scsi_host)
-	max_scsi_hosts--;
-    
+    if(sh->host_no == max_scsi_hosts - 1) {
+	while(--max_scsi_hosts >= next_scsi_host) {
+	    shpnt = scsi_hostlist;
+	    while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
+		shpnt = shpnt->next;
+	    if(shpnt)
+		break;
+	}
+    }
     next_scsi_host--;
     scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
 }
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- v2.0.35/linux/drivers/scsi/in2000.c	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/in2000.c	Sun Nov 15 10:33:09 1998
@@ -122,28 +122,12 @@
 #include "sd.h"
 #include "hosts.h"
 
-
-#define IN2000_VERSION    "1.32"
-#define IN2000_DATE       "28/March/1998"
-
-/*
- * 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 IN2000_VERSION    "1.33"
+#define IN2000_DATE       "26/August/1998"
 
 #include "in2000.h"
 
 
-
 /*
  * 'setup_strings' is a single string used to pass operating parameters and
  * settings from the kernel/module command-line to the driver. 'setup_args[]'
@@ -403,6 +387,10 @@
 
    cmd->SCp.Status = ILLEGAL_STATUS_BYTE;
 
+/* We need to disable interrupts before messing with the input
+ * queue and calling in2000_execute().
+ */
+
    save_flags(flags);
    cli();
 
@@ -442,20 +430,19 @@
  * already connected, we give up immediately. Otherwise, look through
  * the input_Q, using the first command we find that's intended
  * for a currently non-busy target/lun.
+ * Note that this function is always called with interrupts already
+ * disabled (either from in2000_queuecommand() or in2000_intr()).
  */
 static void in2000_execute (struct Scsi_Host *instance)
 {
 struct IN2000_hostdata *hostdata;
 Scsi_Cmnd *cmd, *prev;
-unsigned long flags;
 int i;
 unsigned short *sp;
 unsigned short f;
 unsigned short flushbuf[16];
 
 
-   save_flags(flags);
-   cli();
    hostdata = (struct IN2000_hostdata *)instance->hostdata;
 
 DB(DB_EXECUTE,printk("EX("))
@@ -464,7 +451,6 @@
 
 DB(DB_EXECUTE,printk(")EX-0 "))
 
-      restore_flags(flags);
       return;
       }
 
@@ -488,7 +474,6 @@
 
 DB(DB_EXECUTE,printk(")EX-1 "))
 
-      restore_flags(flags);
       return;
       }
 
@@ -716,7 +701,6 @@
       
 DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))
 
-   restore_flags(flags);
 }
 
 
@@ -841,15 +825,10 @@
 }
 
 
-/* It appears that the Linux interrupt dispatcher calls this
- * function in a non-reentrant fashion. What that means to us
- * is that we can use an SA_INTERRUPT type of interrupt (which
- * is faster), and do an sti() right away to let timer, serial,
- * etc. ints happen.
- *
- * WHOA! Wait a minute, pardner! Does this hold when more than
- * one card has been detected?? I doubt it. Maybe better
- * re-think the multiple card capability....
+/* We need to use spin_lock_irqsave() & spin_unlock_irqrestore() in this
+ * function in order to work in an SMP environment. (I'd be surprised
+ * if the driver is ever used by anyone on a real multi-CPU motherboard,
+ * but it _does_ need to be able to compile and run in an SMP kernel.)
  */
 
 static void in2000_intr (int irqnum, void * dev_id, struct pt_regs *ptregs)
@@ -857,12 +836,12 @@
 struct Scsi_Host *instance;
 struct IN2000_hostdata *hostdata;
 Scsi_Cmnd *patch, *cmd;
-unsigned long flags;
 uchar asr, sr, phs, id, lun, *ucp, msg;
 int i,j;
 unsigned long length;
 unsigned short *sp;
 unsigned short f;
+unsigned long flags;
 
    for (instance = instance_list; instance; instance = instance->next) {
       if (instance->irq == irqnum)
@@ -874,10 +853,9 @@
       }
    hostdata = (struct IN2000_hostdata *)instance->hostdata;
 
-/* OK - it should now be safe to re-enable system interrupts */
+/* Get the spin_lock and disable further ints, for SMP */
 
-   save_flags(flags);
-   sti();
+   CLISPIN_LOCK(flags);
 
 #ifdef PROC_STATISTICS
    hostdata->int_cnt++;
@@ -1013,7 +991,9 @@
             }
 
       write1_io(0, IO_LED_OFF);
-      restore_flags(flags);
+
+/* release the SMP spin_lock and restore irq state */
+      CLISPIN_UNLOCK(flags);
       return;
       }
 
@@ -1029,7 +1009,9 @@
    if (!cmd && (sr != CSR_RESEL_AM && sr != CSR_TIMEOUT && sr != CSR_SELECT)) {
       printk("\nNR:wd-intr-1\n");
       write1_io(0, IO_LED_OFF);
-      restore_flags(flags);
+
+/* release the SMP spin_lock and restore irq state */
+      CLISPIN_UNLOCK(flags);
       return;
       }
 
@@ -1095,7 +1077,6 @@
       case CSR_TIMEOUT:
 DB(DB_INTR,printk("TIMEOUT"))
 
-         cli();
          if (hostdata->state == S_RUNNING_LEVEL2)
             hostdata->connected = NULL;
          else {
@@ -1113,7 +1094,6 @@
  * are commands waiting to be executed.
  */
 
-         sti();
          in2000_execute(instance);
          break;
 
@@ -1121,7 +1101,6 @@
 /* Note: this interrupt should not occur in a LEVEL2 command */
 
       case CSR_SELECT:
-         cli();
 DB(DB_INTR,printk("SELECT"))
          hostdata->connected = cmd = (Scsi_Cmnd *)hostdata->selecting;
 CHECK_NULL(cmd,"csr_select")
@@ -1211,7 +1190,6 @@
       case CSR_SRV_REQ  |PHS_MESS_IN:
 DB(DB_INTR,printk("MSG_IN="))
 
-         cli();
          msg = read_1_byte(hostdata);
          sr = read_3393(hostdata,WD_SCSI_STATUS);  /* clear interrupt */
 
@@ -1360,7 +1338,6 @@
 /* Note: this interrupt will occur only after a LEVEL2 command */
 
       case CSR_SEL_XFER_DONE:
-         cli();
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1387,7 +1364,6 @@
  * there are commands waiting to be executed.
  */
 
-            sti();
             in2000_execute(instance);
             }
          else {
@@ -1446,7 +1422,6 @@
  * so we treat it as a normal command-complete-disconnect.
  */
 
-         cli();
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1456,6 +1431,9 @@
          if (cmd == NULL) {
             printk(" - Already disconnected! ");
             hostdata->state = S_UNCONNECTED;
+
+/* release the SMP spin_lock and restore irq state */
+            CLISPIN_UNLOCK(flags);
             return;
             }
 DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
@@ -1472,13 +1450,11 @@
  * there are commands waiting to be executed.
  */
 
-         sti();
          in2000_execute(instance);
          break;
 
 
       case CSR_DISC:
-         cli();
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1523,7 +1499,6 @@
  * there are commands waiting to be executed.
  */
 
-         sti();
          in2000_execute(instance);
          break;
 
@@ -1531,8 +1506,6 @@
       case CSR_RESEL_AM:
 DB(DB_INTR,printk("RESEL"))
 
-         cli();
-
    /* First we have to make sure this reselection didn't */
    /* happen during Arbitration/Selection of some other device. */
    /* If yes, put losing command back on top of input_Q. */
@@ -1632,10 +1605,12 @@
       }
 
    write1_io(0, IO_LED_OFF);
-   restore_flags(flags);
 
 DB(DB_INTR,printk("} "))
 
+/* release the SMP spin_lock and restore irq state */
+   CLISPIN_UNLOCK(flags);
+
 }
 
 
@@ -1823,7 +1798,6 @@
       cmd->result = DID_ABORT << 16;
       cmd->scsi_done(cmd);
 
-/*      sti();*/
       in2000_execute (instance);
 
       restore_flags(flags);
@@ -1854,7 +1828,6 @@
  * broke.
  */
 
-/*   sti();*/
    in2000_execute (instance);
 
    restore_flags(flags);
@@ -1872,7 +1845,7 @@
 static char setup_used[MAX_SETUP_ARGS];
 static int done_setup = 0;
 
-void in2000_setup (char *str, int *ints)
+in2000__INITFUNC( void in2000_setup (char *str, int *ints) )
 {
 int i;
 char *p1,*p2;
@@ -1904,7 +1877,7 @@
 /* check_setup_args() returns index if key found, 0 if not
  */
 
-static int check_setup_args(char *key, int *flags, int *val, char *buf)
+in2000__INITFUNC( static int check_setup_args(char *key, int *flags, int *val, char *buf) )
 {
 int x;
 char *cp;
@@ -1936,21 +1909,21 @@
  * 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[] = {
+static const unsigned int *bios_tab[] in2000__INITDATA = {
    (unsigned int *)0xc8000,
    (unsigned int *)0xd0000,
    (unsigned int *)0xd8000,
    0
    };
 
-static const unsigned short base_tab[] = {
+static const unsigned short base_tab[] in2000__INITDATA = {
    0x220,
    0x200,
    0x110,
    0x100,
    };
 
-static const int int_tab[] = {
+static const int int_tab[] in2000__INITDATA = {
    15,
    14,
    11,
@@ -1958,7 +1931,7 @@
    };
 
 
-int in2000_detect(Scsi_Host_Template * tpnt)
+in2000__INITFUNC( int in2000_detect(Scsi_Host_Template * tpnt) )
 {
 struct Scsi_Host *instance;
 struct IN2000_hostdata *hostdata;
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h
--- v2.0.35/linux/drivers/scsi/in2000.h	Mon Jul 13 13:46:35 1998
+++ linux/drivers/scsi/in2000.h	Sun Nov 15 10:33:09 1998
@@ -2,7 +2,7 @@
  *    in2000.h -  Linux device driver definitions for the
  *                Always IN2000 ISA SCSI card.
  *
- *    IMPORTANT: This file is for version 1.32 - 28/Mar/1998
+ *    IMPORTANT: This file is for version 1.33 - 26/Aug/1998
  *
  * Copyright (c) 1996 John Shifflett, GeoLog Consulting
  *    john@geolog.com
@@ -377,10 +377,29 @@
 #define PR_STOP      1<<7
 
 
-int in2000_detect(Scsi_Host_Template *);
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < 0x020100   /* 2.0.xx */
+# define in2000__INITFUNC(function) function
+# define in2000__INIT
+# define in2000__INITDATA
+# define CLISPIN_LOCK(flags)   do { save_flags(flags); cli(); } while(0)
+# define CLISPIN_UNLOCK(flags) restore_flags(flags)
+#else                               /* 2.1.xxx */
+# include <linux/init.h>
+# include <asm/spinlock.h>
+# define in2000__INITFUNC(function) __initfunc(function)
+# define in2000__INIT __init
+# define in2000__INITDATA __initdata
+# define CLISPIN_LOCK(flags)   spin_lock_irqsave(&io_request_lock, flags)
+# define CLISPIN_UNLOCK(flags) spin_unlock_irqrestore(&io_request_lock, flags)
+#endif
+
+
+int in2000_detect(Scsi_Host_Template *) in2000__INIT;
 int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int in2000_abort(Scsi_Cmnd *);
-void in2000_setup(char *, int *);
+void in2000_setup(char *, int *) in2000__INIT;
 int in2000_proc_info(char *, char **, off_t, int, int, int);
 struct proc_dir_entry proc_scsi_in2000;
 int in2000_biosparam(struct scsi_disk *, kdev_t, int *);
@@ -392,6 +411,8 @@
 #define IN2000_CPL      2
 #define IN2000_HOST_ID  7
 
+#if LINUX_VERSION_CODE < 0x020100   /* 2.0.xx */
+
 #define IN2000 {  NULL,                /* link pointer for modules */ \
                   NULL,                /* usage_count for modules */ \
                   &proc_scsi_in2000,   /* pointer to /proc/scsi directory entry */ \
@@ -414,6 +435,26 @@
                   0,                   /* unchecked dma */ \
                   DISABLE_CLUSTERING \
                }
+
+#else       /* 2.1.xxx */
+
+#define IN2000 {  proc_dir:        &proc_scsi_in2000,   /* pointer to /proc/scsi directory entry */ \
+                  proc_info:       in2000_proc_info,    /* pointer to proc info function */ \
+                  name:            "Always IN2000",     /* device name */ \
+                  detect:          in2000_detect,       /* returns number of in2000's found */ \
+                  queuecommand:    in2000_queuecommand, /* queue scsi command, don't wait */ \
+                  abort:           in2000_abort,        /* abort current command */ \
+                  reset:           in2000_reset,        /* reset scsi bus */ \
+                  bios_param:      in2000_biosparam,    /* figures out BIOS parameters for lilo, etc */ \
+                  can_queue:       IN2000_CAN_Q,        /* max commands we can queue up */ \
+                  this_id:         IN2000_HOST_ID,      /* host-adapter scsi id */ \
+                  sg_tablesize:    IN2000_SG,           /* scatter-gather table size */ \
+                  cmd_per_lun:     IN2000_CPL,          /* commands per lun */ \
+                  use_clustering:  DISABLE_CLUSTERING,  /* ENABLE_CLUSTERING may speed things up */ \
+                  use_new_eh_code: 0                    /* new error code - not using it yet */ \
+                }
+
+#endif
 
 
 #endif /* IN2000_H */
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c
--- v2.0.35/linux/drivers/scsi/megaraid.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/megaraid.c	Sun Nov 15 10:33:09 1998
@@ -0,0 +1,1347 @@
+/*===================================================================
+ *
+ *                    Linux MegaRAID device driver
+ * 
+ * Copyright 1998 American Megatrends Inc.
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Version : 0.92
+ * 
+ * Description: Linux device driver for AMI MegaRAID controller
+ *
+ * History:
+ *
+ * Version 0.90:
+ *     Works and has been tested with the MegaRAID 428 controller, and
+ *     the MegaRAID 438 controller.  Probably works with the 466 also,
+ *     but not tested.
+ *
+ * Version 0.91:
+ *     Aligned mailbox area on 16-byte boundry.
+ *     Added schedule() at the end to properly clean up.
+ *     Made improvements for conformity to linux driver standards.
+ *
+ * Version 0.92:
+ *     Added support for 2.1 kernels.
+ *         Reads from pci_dev struct, so it's not dependent on pcibios.
+ *         Added some missing virt_to_bus() translations.
+ *     Added support for SMP.
+ *         Changed global cli()'s to spinlocks for 2.1, and simulated
+ *          spinlocks for 2.0.
+ *     Removed setting of SA_INTERRUPT flag when requesting Irq.
+ *
+ * Version 0.92ac:
+ *	Small changes to the comments/formatting. Plus a couple of
+ *	added notes. Returned to the authors. No actual code changes
+ *	save printk levels.
+ *	8 Oct 98	Alan Cox <alan.cox@linux.org>
+ *
+ * BUGS:
+ *     Tested with 2.1.90, but unfortunately there is a bug in pci.c which
+ *     fails to detect our controller.  Does work with 2.1.118--don't know
+ *     which kernel in between it was fixed in.
+ *     With SMP enabled under 2.1.118 with more than one processor, gets an
+ *     error message "scsi_end_request: buffer-list destroyed" under heavy
+ *     IO, but doesn't seem to affect operation, or data integrity.  The
+ *     message doesn't occur without SMP enabled, or with one proccessor with
+ *     SMP enabled, or under any combination under 2.0 kernels.
+ *
+ *===================================================================*/
+#define QISR 1
+
+#define CRLFSTR "\n"
+
+#define MULTIQ 1
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+char kernel_version[] = UTS_RELEASE;
+
+/* originally ported by Dell Corporation; updated, released, and maintained by
+   American Megatrends */
+MODULE_AUTHOR("American Megatrends Inc."); 
+MODULE_DESCRIPTION("AMI MegaRAID driver");    
+#endif
+#endif
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include <linux/wait.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>	/* for kmalloc() */
+#include <linux/config.h>	/* for CONFIG_PCI */
+#if LINUX_VERSION_CODE < 0x20100
+#include <linux/bios32.h>
+#else
+#include <asm/spinlock.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+
+#include "megaraid.h"
+
+/*================================================================
+ *
+ *                          #Defines
+ *
+ *================================================================*/
+
+#if LINUX_VERSION_CODE < 0x020100
+#define ioremap vremap
+#define iounmap vfree
+
+/* simulate spin locks */
+typedef struct {volatile char lock;} spinlock_t;
+#define spin_lock_init(x) { (x)->lock = 0;}
+#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\
+                                        (x)->lock=1; save_flags(flags);\
+                                        cli();}
+#define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);}
+
+#endif
+
+#if LINUX_VERSION_CODE >= 0x020100
+#define queue_task_irq(a,b)     queue_task(a,b)
+#define queue_task_irq_off(a,b) queue_task(a,b)
+#endif
+
+#define MAX_SERBUF 160
+#define COM_BASE 0x2f8
+
+#define ENQUEUE(obj,type,list,next) \
+{ type **node; long cpuflag; \
+  spin_lock_irqsave(&mega_lock,cpuflag);\
+  for(node=&(list); *node; node=(type **)&(*node)->##next); \
+  (*node) = obj; \
+  (*node)->##next = NULL; \
+  spin_unlock_irqrestore(&mega_lock,cpuflag);\
+};
+
+#define DEQUEUE(obj,type,list,next) \
+{ long cpuflag; \
+  spin_lock_irqsave(&mega_lock,cpuflag);\
+  if ((obj=list) != NULL) {\
+    list = (type *)(list)->##next; \
+  } \
+  spin_unlock_irqrestore(&mega_lock,cpuflag);\
+};
+
+u_long RDINDOOR(mega_host_config *megaCfg)
+{
+  return readl(megaCfg->base + 0x20);
+}
+
+void WRINDOOR(mega_host_config *megaCfg, u_long value)
+{
+  writel(value,megaCfg->base+0x20);
+}
+
+u_long RDOUTDOOR(mega_host_config *megaCfg)
+{
+  return readl(megaCfg->base+0x2C);
+}
+
+void WROUTDOOR(mega_host_config *megaCfg, u_long value)
+{
+  writel(value,megaCfg->base+0x2C);
+}
+
+/*================================================================
+ *
+ *                    Function prototypes
+ *
+ *================================================================*/
+static int  MegaIssueCmd(mega_host_config *megaCfg,
+			 u_char *mboxData,
+			 mega_scb *scb,
+			 int intr);
+static int  build_sglist(mega_host_config *megaCfg, mega_scb *scb, 
+			 u_long *buffer, u_long *length);
+
+static void mega_runque(void *);
+static void mega_rundoneq(void);
+static void mega_cmd_done(mega_host_config *,mega_scb *, int);
+
+/* set SERDEBUG to 1 to enable serial debugging */
+#define SERDEBUG 0
+#if SERDEBUG
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int  ser_printk(const char *fmt, ...);
+#endif
+
+/*================================================================
+ *
+ *                    Global variables
+ *
+ *================================================================*/
+static int               numCtlrs = 0;
+static mega_host_config *megaCtlrs[4] = { 0 };
+
+/* Change this to 0 if you want to see the raw drives */
+static int use_raid   = 1;
+
+/* Queue of pending/completed SCBs */
+static mega_scb  *qPending   = NULL;
+static Scsi_Cmnd *qCompleted = NULL;
+
+volatile static spinlock_t mega_lock;
+static struct tq_struct runq = {0,0,mega_runque,NULL};
+
+struct proc_dir_entry proc_scsi_megaraid = {
+  PROC_SCSI_MEGARAID, 8, "megaraid",
+  S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#if SERDEBUG
+static char strbuf[MAX_SERBUF+1];
+
+static void ser_init()
+{
+    unsigned port=COM_BASE;
+
+    outb(0x80,port+3);
+    outb(0,port+1);
+    /* 9600 Baud, if 19200: outb(6,port) */
+    outb(12, port);
+    outb(3,port+3);
+    outb(0,port+1);
+}
+
+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;
+    long flags;
+
+    spin_lock_irqsave(mega_lock,flags);
+    va_start(args,fmt);
+    i = vsprintf(strbuf,fmt,args);
+    ser_puts(strbuf);
+    va_end(args);
+    spin_unlock_irqrestore(&mega_lock,flags);
+
+    return i;
+}
+
+#define TRACE(a)    { ser_printk a;}
+
+#else
+#define TRACE(A)
+#endif
+
+void callDone(Scsi_Cmnd *SCpnt)
+{
+  if (SCpnt->result) {
+    TRACE(("*** %.08lx %.02x <%d.%d.%d> = %x\n", SCpnt->serial_number, 
+	   SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun, 
+	   SCpnt->result));
+  }
+  SCpnt->scsi_done(SCpnt);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ *                      Local functions
+ *
+ *-------------------------------------------------------------------------*/
+
+/*================================================
+ * Initialize SCB structures
+ *================================================*/
+static void initSCB(mega_host_config *megaCfg)
+{
+  int idx;
+
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    megaCfg->scbList[idx].idx    = -1;
+    megaCfg->scbList[idx].flag   = 0;
+    megaCfg->scbList[idx].sgList = NULL;
+    megaCfg->scbList[idx].SCpnt  = NULL;
+  }
+}
+
+/*===========================
+ * Allocate a SCB structure
+ *===========================*/
+static mega_scb *allocateSCB(mega_host_config *megaCfg,Scsi_Cmnd *SCpnt)
+{
+  int        idx;
+  long       flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    if (megaCfg->scbList[idx].idx < 0) {
+
+      /* Set Index and SCB pointer */ 
+      megaCfg->scbList[idx].flag  = 0;
+      megaCfg->scbList[idx].idx   = idx;
+      megaCfg->scbList[idx].SCpnt = SCpnt;
+      megaCfg->scbList[idx].next  = NULL;
+      spin_unlock_irqrestore(&mega_lock,flags);
+
+      if (megaCfg->scbList[idx].sgList == NULL) {
+	megaCfg->scbList[idx].sgList =
+                  kmalloc(sizeof(mega_sglist)*MAX_SGLIST,GFP_ATOMIC|GFP_DMA);
+      }
+
+      return &megaCfg->scbList[idx];
+    }
+  }
+  spin_unlock_irqrestore(&mega_lock,flags);
+
+  printk(KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n");
+  
+  return NULL;
+}
+
+/*=======================
+ * Free a SCB structure
+ *=======================*/
+static void freeSCB(mega_scb *scb)
+{
+  long flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+  scb->flag  = 0;
+  scb->idx   = -1;
+  scb->next  = NULL;
+  scb->SCpnt = NULL;
+  spin_unlock_irqrestore(&mega_lock,flags);
+}
+
+/* Run through the list of completed requests */
+static void mega_rundoneq()
+{
+  mega_host_config *megaCfg;
+  Scsi_Cmnd        *SCpnt;
+  long              islogical;
+
+  while(1) {
+    DEQUEUE(SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+    if (SCpnt == NULL) return;
+
+    megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+    /* Check if we're allowing access to RAID drives or physical
+     *  if use_raid == 1 and this wasn't a disk on the max channel or
+     *  if use_raid == 0 and this was a disk on the max channel
+     *  then fail.
+     */
+    islogical = (SCpnt->channel == megaCfg->host->max_channel) ? 1 : 0;
+    if (SCpnt->cmnd[0] == INQUIRY &&
+	((((u_char*)SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) &&
+	(islogical != use_raid)) {
+       SCpnt->result = 0xF0;
+    }
+
+    /* Convert result to error */
+    switch(SCpnt->result) {
+    case 0x00: case 0x02:
+      SCpnt->result |= (DID_OK << 16);
+      break;
+    case 0x8:
+      SCpnt->result |= (DID_BUS_BUSY << 16);
+      break;
+    default:
+      SCpnt->result |= (DID_BAD_TARGET << 16);
+      break;
+    }
+
+    /* Callback */
+    callDone(SCpnt);
+  }
+}
+
+/* Add command to the list of completed requests */
+static void mega_cmd_done(mega_host_config *megaCfg,mega_scb *pScb, int status)
+{
+  pScb->SCpnt->result = status;
+  ENQUEUE(pScb->SCpnt, Scsi_Cmnd, qCompleted, host_scribble);
+  freeSCB(pScb);
+}
+
+/*----------------------------------------------------
+ * Process pending queue list
+ *
+ * Run as a scheduled task 
+ *----------------------------------------------------*/
+static void mega_runque(void *dummy)
+{
+  mega_host_config *megaCfg;
+  mega_scb         *pScb;
+  long              flags;
+
+  /* Take care of any completed requests */
+  mega_rundoneq();
+
+  DEQUEUE(pScb,mega_scb,qPending,next);
+
+  if (pScb) {
+    megaCfg = (mega_host_config *)pScb->SCpnt->host->hostdata;
+
+    if (megaCfg->mbox->busy || megaCfg->flag & (IN_ISR|PENDING)) {
+      printk(KERN_DEBUG "PENDING = %x, IN_ISR = %x, mbox.busy = %x\n",(u_int)(megaCfg->flag
+            & PENDING), (u_int)(megaCfg->flag & IN_ISR), megaCfg->mbox->busy);
+      TRACE(("%.08lx %.02x <%d.%d.%d> intr%d busy%d isr%d pending%d\n",
+	     pScb->SCpnt->serial_number,
+	     pScb->SCpnt->cmnd[0],
+	     pScb->SCpnt->channel,
+	     pScb->SCpnt->target,
+	     pScb->SCpnt->lun,
+	     intr_count,
+	     megaCfg->mbox->busy,
+	     (megaCfg->flag & IN_ISR)  ? 1 : 0,
+	     (megaCfg->flag & PENDING) ? 1 : 0));
+    }
+
+    if (MegaIssueCmd(megaCfg, pScb->mboxData, pScb, 1)) {
+      printk(KERN_DEBUG "MegaIssueCmd returned BUSY.  Rescheduling command.\n");
+      /* We're BUSY... come back later */
+      spin_lock_irqsave(&mega_lock,flags);
+      pScb->next = qPending;
+      qPending   = pScb;
+      spin_unlock_irqrestore(&mega_lock,flags);
+
+      if (!(megaCfg->flag & PENDING)) { /* If PENDING, irq will schedule task */
+          queue_task(&runq, &tq_scheduler);
+      }
+    }
+  }
+}
+
+/*-------------------------------------------------------------------
+ *
+ *                 Build a SCB from a Scsi_Cmnd
+ *
+ * Returns a SCB pointer, or NULL
+ * If NULL is returned, the scsi_done function MUST have been called
+ *
+ *-------------------------------------------------------------------*/
+static mega_scb *mega_build_cmd(mega_host_config *megaCfg, Scsi_Cmnd *SCpnt)
+{
+  mega_scb      *pScb;
+  mega_mailbox  *mbox;
+  mega_passthru *pthru;
+  long           seg;
+
+  /* We don't support multi-luns */
+  if (SCpnt->lun != 0) {
+    SCpnt->result = (DID_BAD_TARGET << 16);
+    callDone(SCpnt);
+    return NULL;
+  }
+
+  /*-----------------------------------------------------
+   *
+   *               Logical drive commands
+   *
+   *-----------------------------------------------------*/
+  if (SCpnt->channel == megaCfg->host->max_channel) {
+    switch(SCpnt->cmnd[0]) {
+    case TEST_UNIT_READY:
+      memset(SCpnt->request_buffer, 0, SCpnt->request_bufflen);
+      SCpnt->result = (DID_OK << 16);
+      callDone(SCpnt);
+      return NULL;
+
+    case MODE_SENSE:
+      memset(SCpnt->request_buffer, 0, SCpnt->cmnd[4]);
+      SCpnt->result = (DID_OK << 16);
+      callDone(SCpnt);
+      return NULL;
+
+    case READ_CAPACITY:
+    case INQUIRY:
+      /* Allocate a SCB and initialize passthru */
+      if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+	SCpnt->result = (DID_ERROR << 16);
+	callDone(SCpnt);
+	return NULL;
+      }
+      pthru = &pScb->pthru;
+      mbox  = (mega_mailbox *)&pScb->mboxData;
+
+      memset(mbox,  0, sizeof(pScb->mboxData));
+      memset(pthru, 0, sizeof(mega_passthru));
+      pthru->timeout      = 0;
+      pthru->ars          = 0;
+      pthru->islogical    = 1;
+      pthru->logdrv       = SCpnt->target;
+      pthru->cdblen       = SCpnt->cmd_len;
+      pthru->dataxferaddr = virt_to_bus(SCpnt->request_buffer);
+      pthru->dataxferlen  = SCpnt->request_bufflen;
+      memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+      /* Initialize mailbox area */
+      mbox->cmd      = MEGA_MBOXCMD_PASSTHRU;
+      mbox->xferaddr = virt_to_bus(pthru);
+
+      return pScb;
+
+    case READ_6:
+    case WRITE_6:
+    case READ_10:
+    case WRITE_10:
+      /* Allocate a SCB and initialize mailbox */
+      if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+	SCpnt->result = (DID_ERROR << 16);
+	callDone(SCpnt);
+	return NULL;
+      }
+      mbox = (mega_mailbox *)&pScb->mboxData;
+
+      memset(mbox, 0, sizeof(pScb->mboxData));
+      mbox->logdrv = SCpnt->target;
+      mbox->cmd    = (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10) ?
+	MEGA_MBOXCMD_LREAD : MEGA_MBOXCMD_LWRITE;
+      
+      /* 6-byte */
+      if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) {
+	mbox->numsectors = 
+	  (u_long)SCpnt->cmnd[4];
+	mbox->lba = 
+	  ((u_long)SCpnt->cmnd[1] << 16) |
+	  ((u_long)SCpnt->cmnd[2] << 8) |
+	  (u_long)SCpnt->cmnd[3];
+	mbox->lba &= 0x1FFFFF;
+      }
+      
+      /* 10-byte */
+      if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) {
+	mbox->numsectors = 
+	  (u_long)SCpnt->cmnd[8] |
+	  ((u_long)SCpnt->cmnd[7] << 8);
+	mbox->lba =
+	  ((u_long)SCpnt->cmnd[2] << 24) |
+	  ((u_long)SCpnt->cmnd[3] << 16) |
+	  ((u_long)SCpnt->cmnd[4] << 8) |
+	  (u_long)SCpnt->cmnd[5];
+      }
+      
+      /* Calculate Scatter-Gather info */
+      mbox->numsgelements = build_sglist(megaCfg, pScb, 
+					 (u_long*)&mbox->xferaddr,
+					 (u_long*)&seg);
+
+      return pScb;
+      
+    default:
+      SCpnt->result = (DID_BAD_TARGET << 16);
+      callDone(SCpnt);
+      return NULL;
+    }
+  }
+  /*-----------------------------------------------------
+   *
+   *               Passthru drive commands
+   *
+   *-----------------------------------------------------*/
+  else {
+    /* Allocate a SCB and initialize passthru */
+    if ((pScb = allocateSCB(megaCfg,SCpnt)) == NULL) {
+      SCpnt->result = (DID_ERROR << 16);
+      callDone(SCpnt);
+      return NULL;
+    }
+    pthru = &pScb->pthru;
+    mbox  = (mega_mailbox *)pScb->mboxData;
+    
+    memset(mbox,  0, sizeof(pScb->mboxData));
+    memset(pthru, 0, sizeof(mega_passthru));
+    pthru->timeout   = 0;
+    pthru->ars       = 0;
+    pthru->islogical = 0;
+    pthru->channel   = SCpnt->channel;
+    pthru->target    = SCpnt->target;
+    pthru->cdblen    = SCpnt->cmd_len;
+    memcpy(pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+   
+    pthru->numsgelements = build_sglist(megaCfg, pScb,
+					(u_long *)&pthru->dataxferaddr,
+					(u_long *)&pthru->dataxferlen);
+    
+    /* Initialize mailbox */
+    mbox->cmd      = MEGA_MBOXCMD_PASSTHRU;
+    mbox->xferaddr = virt_to_bus(pthru);
+
+    return pScb;
+  }
+  return NULL;
+}
+
+/*--------------------------------------------------------------------
+ * Interrupt service routine
+ *--------------------------------------------------------------------*/
+static void megaraid_isr(int irq, void *devp, struct pt_regs *regs)
+{
+  mega_host_config *megaCfg;
+  u_char            byte, idx, sIdx;
+  u_long            dword;
+  mega_mailbox     *mbox;
+  mega_scb         *pScb;
+  long              flags;
+  int               qCnt, qStatus;
+
+  megaCfg = (mega_host_config *)devp;
+  mbox    = (mega_mailbox *)megaCfg->mbox;
+
+  if (megaCfg->host->irq == irq) {
+    spin_lock_irqsave(&mega_lock,flags);
+
+    if (megaCfg->flag & IN_ISR) {
+      TRACE(("ISR called reentrantly!!\n"));
+    }
+
+    megaCfg->flag |= IN_ISR;
+
+    /* Check if a valid interrupt is pending */
+    if (megaCfg->flag & BOARD_QUARTZ) {
+        dword = RDOUTDOOR(megaCfg);
+        if (dword != 0x10001234) {
+            /* Spurious interrupt */
+            megaCfg->flag &= ~IN_ISR;
+            spin_unlock_irqrestore(&mega_lock,flags);
+            return;
+        }
+        WROUTDOOR(megaCfg,dword);
+    } else {
+        byte = READ_PORT(megaCfg->host->io_port, INTR_PORT);
+        if ((byte & VALID_INTR_BYTE) == 0) {
+          /* Spurious interrupt */
+          megaCfg->flag &= ~IN_ISR;
+          spin_unlock_irqrestore(&mega_lock,flags);
+          return;
+        }
+        WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
+    }
+    
+    qCnt    = mbox->numstatus;
+    qStatus = mbox->status;
+
+    if (qCnt > 1) {TRACE(("ISR: Received %d status\n", qCnt))
+        printk(KERN_DEBUG "Got numstatus = %d\n",qCnt);
+    }
+    
+    for(idx=0; idx<qCnt; idx++) {
+      sIdx = mbox->completed[idx];
+      if (sIdx > 0) {
+	pScb = &megaCfg->scbList[sIdx-1];
+        spin_unlock_irqrestore(&mega_lock,flags); /* locks within cmd_done */
+	mega_cmd_done(megaCfg,&megaCfg->scbList[sIdx-1], qStatus);
+        spin_lock_irqsave(&mega_lock,flags);
+      }
+    }
+    if (megaCfg->flag & BOARD_QUARTZ) {
+        WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox)|0x2);
+        while (RDINDOOR(megaCfg) & 0x02);
+    } else {
+        CLEAR_INTR(megaCfg->host->io_port);
+    }
+
+    megaCfg->flag &= ~IN_ISR;
+    megaCfg->flag &= ~PENDING;
+
+    /* Queue as a delayed ISR routine */
+    queue_task_irq_off(&runq, &tq_immediate);
+    mark_bh(IMMEDIATE_BH);
+    spin_unlock_irqrestore(&mega_lock,flags);
+
+  }
+}
+
+/*==================================================*/
+/* Wait until the controller's mailbox is available */
+/*==================================================*/
+static int busyWaitMbox(mega_host_config *megaCfg)
+{
+  mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
+  long          counter;
+
+  for(counter=0; counter<0xFFFFFF; counter++) {
+    if (!mbox->busy) return 0;
+  }
+  return -1;
+}
+
+/*=====================================================
+ * Post a command to the card
+ *
+ * Arguments:
+ *   mega_host_config *megaCfg - Controller structure
+ *   u_char *mboxData - Mailbox area, 16 bytes
+ *   mega_scb *pScb   - SCB posting (or NULL if N/A)
+ *   int intr         - if 1, interrupt, 0 is blocking
+ *=====================================================*/
+static int MegaIssueCmd(mega_host_config *megaCfg,
+			u_char *mboxData,
+			mega_scb *pScb,
+			int intr)
+{
+  mega_mailbox *mbox = (mega_mailbox *)megaCfg->mbox;
+  long          flags;
+  u_char        byte;
+  u_long        cmdDone;
+
+  mboxData[0x1] = (pScb ? pScb->idx+1 : 0x00);  /* Set cmdid */
+  mboxData[0xF] = 1;                            /* Set busy */
+
+  /* one bad report of problem when issuing a command while pending.
+   * Wasn't able to duplicate, but it doesn't really affect performance
+   * anyway, so don't allow command while PENDING
+   */
+  if (megaCfg->flag & PENDING) {
+    return -1;
+  }
+
+  /* Wait until mailbox is free */
+  if (busyWaitMbox(megaCfg)) {
+    if (pScb) {
+      TRACE(("Mailbox busy %.08lx <%d.%d.%d>\n", pScb->SCpnt->serial_number,
+	     pScb->SCpnt->channel, pScb->SCpnt->target, pScb->SCpnt->lun));
+    }
+    return -1;
+  }
+
+  /* Copy mailbox data into host structure */
+  spin_lock_irqsave(&mega_lock,flags);
+  memset(mbox, 0, sizeof(mega_mailbox));
+  memcpy(mbox, mboxData, 16);
+  spin_unlock_irqrestore(&mega_lock,flags);
+
+  /* Kick IO */
+  megaCfg->flag |= PENDING;
+  if (intr) {
+    /* Issue interrupt (non-blocking) command */
+    if (megaCfg->flag & BOARD_QUARTZ) {
+        mbox->mraid_poll = 0; 
+        mbox->mraid_ack = 0; 
+        WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
+    } else {
+        ENABLE_INTR(megaCfg->host->io_port);
+        ISSUE_COMMAND(megaCfg->host->io_port);
+    }
+  }
+  else {      /* Issue non-ISR (blocking) command */
+
+    if (megaCfg->flag & BOARD_QUARTZ) {
+
+      mbox->mraid_poll = 0; 
+      mbox->mraid_ack = 0; 
+      WRINDOOR(megaCfg, virt_to_bus(megaCfg->mbox) | 0x1);
+
+      while((cmdDone=RDOUTDOOR(megaCfg)) != 0x10001234);
+      WROUTDOOR(megaCfg, cmdDone);
+
+      if (pScb) {
+	mega_cmd_done(megaCfg,pScb, mbox->status);
+	mega_rundoneq();
+      }
+
+      WRINDOOR(megaCfg,virt_to_bus(megaCfg->mbox) | 0x2);
+      while(RDINDOOR(megaCfg) & 0x2);
+
+      megaCfg->flag &= ~PENDING;
+    }
+    else {
+      DISABLE_INTR(megaCfg->host->io_port);
+      ISSUE_COMMAND(megaCfg->host->io_port);
+      
+      while(!((byte=READ_PORT(megaCfg->host->io_port,INTR_PORT))&INTR_VALID));
+      WRITE_PORT(megaCfg->host->io_port, INTR_PORT, byte);
+      
+      ENABLE_INTR(megaCfg->host->io_port);
+      CLEAR_INTR(megaCfg->host->io_port);
+      
+      if (pScb) {
+	mega_cmd_done(megaCfg,pScb, mbox->status);
+	mega_rundoneq();
+      }
+      megaCfg->flag &= ~PENDING;
+    }
+  }
+
+  return 0;
+}
+
+/*-------------------------------------------------------------------
+ * Copies data to SGLIST
+ *-------------------------------------------------------------------*/
+static int build_sglist(mega_host_config *megaCfg, mega_scb *scb, 
+			u_long *buffer, u_long *length)
+{
+  struct scatterlist *sgList;
+  int idx;
+
+  /* Scatter-gather not used */
+  if (scb->SCpnt->use_sg == 0) {
+    *buffer = virt_to_bus(scb->SCpnt->request_buffer);
+    *length = (u_long)scb->SCpnt->request_bufflen;
+    return 0;
+  }
+
+  sgList = (struct scatterlist *)scb->SCpnt->buffer;
+  if (scb->SCpnt->use_sg == 1) {
+    *buffer = virt_to_bus(sgList[0].address);
+    *length = (u_long)sgList[0].length;
+    return 0;
+  }
+
+  /* Copy Scatter-Gather list info into controller structure */
+  for(idx=0; idx<scb->SCpnt->use_sg; idx++) {
+    scb->sgList[idx].address = virt_to_bus(sgList[idx].address);
+    scb->sgList[idx].length  = (u_long)sgList[idx].length;
+  }
+  
+  /* Reset pointer and length fields */
+  *buffer = virt_to_bus(scb->sgList);
+  *length = 0;
+
+  /* Return count of SG requests */
+  return scb->SCpnt->use_sg;
+}
+    
+/*--------------------------------------------------------------------
+ * Initializes the adress of the controller's mailbox register
+ *  The mailbox register is used to issue commands to the card.
+ *  Format of the mailbox area:
+ *   00 01 command
+ *   01 01 command id
+ *   02 02 # of sectors
+ *   04 04 logical bus address
+ *   08 04 physical buffer address
+ *   0C 01 logical drive #
+ *   0D 01 length of scatter/gather list
+ *   0E 01 reserved
+ *   0F 01 mailbox busy
+ *   10 01 numstatus byte
+ *   11 01 status byte
+ *--------------------------------------------------------------------*/
+static int mega_register_mailbox(mega_host_config *megaCfg, u_long paddr)
+{
+  /* align on 16-byte boundry */
+  megaCfg->mbox = &megaCfg->mailbox;
+  megaCfg->mbox = (mega_mailbox *) ((((ulong)megaCfg->mbox) + 16)&0xfffffff0);
+  paddr = (paddr+16)&0xfffffff0;
+
+  /* Register mailbox area with the firmware */
+  if (megaCfg->flag & BOARD_QUARTZ) {
+  }
+  else {
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT0, paddr         & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT1, (paddr >>  8) & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT2, (paddr >> 16) & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, MBOX_PORT3, (paddr >> 24) & 0xFF);
+    WRITE_PORT(megaCfg->host->io_port, ENABLE_MBOX_REGION, ENABLE_MBOX_BYTE);
+    
+    CLEAR_INTR(megaCfg->host->io_port);
+    ENABLE_INTR(megaCfg->host->io_port);
+  }
+  return 0;
+}
+
+/*-------------------------------------------------------------------
+ * Issue an adapter info query to the controller
+ *-------------------------------------------------------------------*/
+static int mega_i_query_adapter(mega_host_config *megaCfg)
+{
+  mega_RAIDINQ *adapterInfo;
+  mega_mailbox *mbox;
+  u_char        mboxData[16];
+  u_long        paddr;
+
+  spin_lock_init(&mega_lock);
+  /* Initialize adapter inquiry */
+  paddr = virt_to_bus(megaCfg->mega_buffer);
+  mbox  = (mega_mailbox *)mboxData;
+
+  memset((void *)megaCfg->mega_buffer, 0, sizeof(megaCfg->mega_buffer));
+  memset(mbox, 0, 16);
+
+  /* Initialize mailbox registers */
+  mbox->cmd      = MEGA_MBOXCMD_ADAPTERINQ;
+  mbox->xferaddr = paddr;
+
+  /* Issue a blocking command to the card */
+  MegaIssueCmd(megaCfg, mboxData, NULL, 0);
+  
+  /* Initialize host/local structures with Adapter info */
+  adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+  megaCfg->host->max_channel = adapterInfo->AdpInfo.ChanPresent;
+  megaCfg->host->max_id      = adapterInfo->AdpInfo.MaxTargPerChan;
+  megaCfg->numldrv           = adapterInfo->LogdrvInfo.NumLDrv;
+
+#if 0
+  printk(KERN_DEBUG "---- Logical drive info ----\n");
+  for(i=0; i<megaCfg->numldrv; i++) {
+    printk(KERN_DEBUG "%d: size: %ld prop: %x state: %x\n",i,
+	   adapterInfo->LogdrvInfo.LDrvSize[i],
+	   adapterInfo->LogdrvInfo.LDrvProp[i],
+	   adapterInfo->LogdrvInfo.LDrvState[i]);
+  }
+  printk(KERN_DEBUG "---- Physical drive info ----\n");
+  for(i=0; i<MAX_PHYSICAL_DRIVES; i++) {
+    if (i && !(i % 8)) printk("\n");
+    printk("%d: %x   ", i, adapterInfo->PhysdrvInfo.PDrvState[i]);
+  }
+  printk("\n");
+#endif
+
+  megaCfg->max_cmds = adapterInfo->AdpInfo.MaxConcCmds;
+
+#ifdef HP            /* use HP firmware and bios version encoding */
+      sprintf(megaCfg->fwVer,"%c%d%d.%d%d",
+          adapterInfo->AdpInfo.FwVer[2],
+          adapterInfo->AdpInfo.FwVer[1] >> 8,
+          adapterInfo->AdpInfo.FwVer[1] & 0x0f,
+          adapterInfo->AdpInfo.FwVer[2] >> 8,
+          adapterInfo->AdpInfo.FwVer[2] & 0x0f);
+      sprintf(megaCfg->biosVer,"%c%d%d.%d%d",
+          adapterInfo->AdpInfo.BiosVer[2],
+          adapterInfo->AdpInfo.BiosVer[1] >> 8,
+          adapterInfo->AdpInfo.BiosVer[1] & 0x0f,
+          adapterInfo->AdpInfo.BiosVer[2] >> 8,
+          adapterInfo->AdpInfo.BiosVer[2] & 0x0f);
+#else
+      memcpy(megaCfg->fwVer, adapterInfo->AdpInfo.FwVer, 4);
+      megaCfg->fwVer[4] = 0;
+
+      memcpy(megaCfg->biosVer, adapterInfo->AdpInfo.BiosVer, 4);
+      megaCfg->biosVer[4] = 0;
+#endif
+
+  printk(KERN_INFO "megaraid: [%s:%s] detected %d logical drives" CRLFSTR,
+	 megaCfg->fwVer,
+	 megaCfg->biosVer,
+	 megaCfg->numldrv);
+  return 0;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ *                      Driver interface functions
+ *
+ *-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------
+ * Returns data to be displayed in /proc/scsi/megaraid/X
+ *----------------------------------------------------------*/
+int megaraid_proc_info(char *buffer, char **start, off_t offset,
+		       int length, int inode, int inout)
+{
+  *start = buffer;
+  return 0;
+}
+
+int findCard(Scsi_Host_Template *pHostTmpl, 
+	     u_short pciVendor, u_short pciDev,
+	     long flag)
+{
+  mega_host_config *megaCfg;
+  struct Scsi_Host *host;
+  u_char            pciBus, pciDevFun, megaIrq;
+  u_long            megaBase;
+  u_short           pciIdx = 0;
+
+#if LINUX_VERSION_CODE < 0x20100
+  while(!pcibios_find_device(pciVendor, pciDev, pciIdx,&pciBus,&pciDevFun)) {
+#else
+  struct pci_dev   *pdev=pci_devices;
+
+  while((pdev = pci_find_device(pciVendor, pciDev, pdev))) {
+    pciBus = pdev->bus->number;
+    pciDevFun = pdev->devfn;
+#endif
+    printk(KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:fun %d\n",
+	   pciVendor, 
+	   pciDev, 
+	   pciIdx, pciBus, 
+	   PCI_SLOT(pciDevFun), 
+	   PCI_FUNC(pciDevFun));
+    
+    /* Read the base port and IRQ from PCI */
+#if LINUX_VERSION_CODE < 0x20100
+    pcibios_read_config_dword(pciBus, pciDevFun,
+			     PCI_BASE_ADDRESS_0,
+			     (u_int *)&megaBase);
+    pcibios_read_config_byte(pciBus, pciDevFun,			       
+			     PCI_INTERRUPT_LINE,
+			     &megaIrq);
+#else
+    megaBase = pdev->base_address[0];
+    megaIrq = pdev->irq;
+#endif
+    pciIdx++;
+
+    if (flag & BOARD_QUARTZ) {
+      megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
+      megaBase = (long) ioremap(megaBase,128);
+    }
+    else {
+      megaBase &= PCI_BASE_ADDRESS_IO_MASK;
+      megaBase += 0x10;
+    }
+
+    /* Initialize SCSI Host structure */
+    host    = scsi_register(pHostTmpl, sizeof(mega_host_config));
+    megaCfg = (mega_host_config *)host->hostdata;
+    memset(megaCfg, 0, sizeof(mega_host_config));
+
+    printk(KERN_INFO " scsi%d: Found a MegaRAID controller at 0x%x, IRQ: %d" CRLFSTR, 
+	   host->host_no, (u_int)megaBase, megaIrq);
+    
+    /* Copy resource info into structure */
+    megaCfg->flag            = flag;
+    megaCfg->host            = host;
+    megaCfg->base            = megaBase;
+    megaCfg->host->irq       = megaIrq;
+    megaCfg->host->io_port   = megaBase;
+    megaCfg->host->n_io_port = 16;
+    megaCfg->host->unique_id = (pciBus << 8) | pciDevFun;
+    megaCtlrs[numCtlrs++]    = megaCfg;
+
+    if (flag != BOARD_QUARTZ) {
+      /* Request our IO Range */
+      if (check_region(megaBase, 16)) {
+	printk(KERN_WARNING "megaraid: Couldn't register I/O range!" CRLFSTR);
+	scsi_unregister(host);
+	continue;
+      }
+      request_region(megaBase, 16, "megaraid");
+    }
+
+    /* Request our IRQ */
+    if (request_irq(megaIrq, megaraid_isr, SA_SHIRQ, 
+		    "megaraid", megaCfg)) {
+      printk(KERN_WARNING "megaraid: Couldn't register IRQ %d!" CRLFSTR,
+	     megaIrq);
+      scsi_unregister(host);
+      continue;
+    }
+
+    mega_register_mailbox(megaCfg, virt_to_bus((void*)&megaCfg->mailbox));
+    mega_i_query_adapter(megaCfg);
+
+    /* Initialize SCBs */
+    initSCB(megaCfg);
+
+  }
+  return pciIdx;
+}
+
+/*---------------------------------------------------------
+ * Detects if a megaraid controller exists in this system
+ *---------------------------------------------------------*/
+int megaraid_detect(Scsi_Host_Template *pHostTmpl)
+{
+  int count = 0;
+
+  pHostTmpl->proc_dir = &proc_scsi_megaraid;
+
+#if LINUX_VERSION_CODE < 0x20100
+  if (!pcibios_present()) 
+    {
+      printk(KERN_WARNING "megaraid: PCI bios not present." CRLFSTR);
+      return 0;
+    }
+#endif
+
+  count += findCard(pHostTmpl, 0x101E, 0x9010, 0);
+  count += findCard(pHostTmpl, 0x101E, 0x9060, 0);
+  count += findCard(pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ);
+
+  return count;
+}
+
+/*---------------------------------------------------------------------
+ * Release the controller's resources
+ *---------------------------------------------------------------------*/
+int megaraid_release(struct Scsi_Host *pSHost)
+{
+  mega_host_config *megaCfg;
+  mega_mailbox         *mbox;
+  u_char                mboxData[16];
+
+  megaCfg = (mega_host_config*)pSHost->hostdata;
+  mbox    = (mega_mailbox *)mboxData;
+
+  /* Flush cache to disk */
+  memset(mbox, 0, 16);
+  mboxData[0] = 0xA;
+
+  /* Issue a blocking (interrupts disabled) command to the card */
+  MegaIssueCmd(megaCfg, mboxData, NULL, 0);
+
+  schedule();
+
+  /* Free our resources */
+  if (megaCfg->flag & BOARD_QUARTZ) {
+      iounmap((void *)megaCfg->base);
+  } else {
+      release_region(megaCfg->host->io_port, 16);
+  }
+  free_irq(megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise
+                                            extra interrupt is generated */
+  scsi_unregister(pSHost);
+
+  return 0;
+}
+
+/*----------------------------------------------
+ * Get information about the card/driver 
+ *----------------------------------------------*/
+const char *megaraid_info(struct Scsi_Host *pSHost)
+{
+  static char           buffer[512];
+  mega_host_config  *megaCfg;
+  mega_RAIDINQ          *adapterInfo;
+
+  megaCfg     = (mega_host_config *)pSHost->hostdata;
+  adapterInfo = (mega_RAIDINQ *)megaCfg->mega_buffer;
+
+  sprintf(buffer, "AMI MegaRAID %s %d commands %d targs %d chans",
+	  megaCfg->fwVer,
+	  adapterInfo->AdpInfo.MaxConcCmds,
+	  megaCfg->host->max_id,
+	  megaCfg->host->max_channel);
+  return buffer;
+}
+
+/*-----------------------------------------------------------------
+ * Perform a SCSI command
+ * Mailbox area:
+ *   00 01 command
+ *   01 01 command id
+ *   02 02 # of sectors
+ *   04 04 logical bus address
+ *   08 04 physical buffer address
+ *   0C 01 logical drive #
+ *   0D 01 length of scatter/gather list
+ *   0E 01 reserved
+ *   0F 01 mailbox busy
+ *   10 01 numstatus byte
+ *   11 01 status byte 
+ *-----------------------------------------------------------------*/
+int megaraid_queue(Scsi_Cmnd *SCpnt, void (*pktComp)(Scsi_Cmnd *))
+{
+  mega_host_config *megaCfg;
+  mega_scb         *pScb;
+
+  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+  if (!(megaCfg->flag & (1L << SCpnt->channel))) {
+    printk(KERN_INFO "scsi%d: scanning channel %c for devices.\n",
+	   megaCfg->host->host_no,
+	   SCpnt->channel + 'A');
+    megaCfg->flag |= (1L << SCpnt->channel);
+  }
+
+  SCpnt->scsi_done = pktComp;
+
+  /* Allocate and build a SCB request */
+  if ((pScb = mega_build_cmd(megaCfg, SCpnt)) != NULL) {
+    /* Add SCB to the head of the pending queue */
+    ENQUEUE(pScb, mega_scb, qPending, next);
+
+    /* Issue the command to the card */
+      mega_runque(NULL);
+  }
+
+  return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Issue a blocking command to the controller
+ *
+ * Note - this isnt 2.0.x SMP safe
+ *----------------------------------------------------------------------*/
+volatile static int internal_done_flag    = 0;
+volatile static int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd *SCpnt)
+{
+  internal_done_errcode = SCpnt->result;
+  internal_done_flag++;
+}
+
+/*
+ *	This seems dangerous in an SMP environment because 
+ *	while spinning on internal_done_flag in 2.0.x SMP
+ *	no IRQ's will be taken, including those that might
+ *	be needed to clear this.
+ *
+ *	I think this should be using a wait queue ?
+ *				-- AC
+ */
+ 
+int megaraid_command(Scsi_Cmnd *SCpnt)
+{
+  internal_done_flag = 0;
+
+  /* Queue command, and wait until it has completed */
+  megaraid_queue(SCpnt, internal_done);
+
+  while(!internal_done_flag)
+    barrier();
+
+  return internal_done_errcode;
+}
+
+/*---------------------------------------------------------------------
+ * Abort a previous SCSI request
+ *---------------------------------------------------------------------*/
+int megaraid_abort(Scsi_Cmnd *SCpnt)
+{
+  mega_host_config *megaCfg;
+  int       idx;
+  long      flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+
+  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+  TRACE(("ABORT!!! %.08lx %.02x <%d.%d.%d>\n",
+	 SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, 
+	 SCpnt->lun));
+  /*
+   * Walk list of SCBs for any that are still outstanding
+   */
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    if (megaCfg->scbList[idx].idx >= 0) {
+      if (megaCfg->scbList[idx].SCpnt == SCpnt) {
+	freeSCB(&megaCfg->scbList[idx]);
+
+	SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
+	callDone(SCpnt);
+      }
+    }
+  }
+  spin_unlock_irqrestore(&mega_lock,flags);
+  return SCSI_ABORT_SNOOZE;
+}
+
+/*---------------------------------------------------------------------
+ * Reset a previous SCSI request
+ *---------------------------------------------------------------------*/
+int megaraid_reset(Scsi_Cmnd *SCpnt, unsigned int rstflags)
+{
+  mega_host_config *megaCfg;
+  int       idx;
+  long      flags;
+
+  spin_lock_irqsave(&mega_lock,flags);
+
+  megaCfg = (mega_host_config *)SCpnt->host->hostdata;
+
+  TRACE(("RESET: %.08lx %.02x <%d.%d.%d>\n",
+	 SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, 
+	 SCpnt->lun));
+
+  /*
+   * Walk list of SCBs for any that are still outstanding
+   */
+  for(idx=0; idx<megaCfg->max_cmds; idx++) {
+    if (megaCfg->scbList[idx].idx >= 0) {
+      SCpnt = megaCfg->scbList[idx].SCpnt;
+      freeSCB(&megaCfg->scbList[idx]);
+      SCpnt->result = (DID_RESET << 16) | (SUGGEST_RETRY<<24);
+      callDone(SCpnt);
+    }
+  }
+  spin_unlock_irqrestore(&mega_lock,flags);
+  return SCSI_RESET_PUNT;
+} 
+
+/*-------------------------------------------------------------
+ * Return the disk geometry for a particular disk
+ * Input:
+ *   Disk *disk - Disk geometry
+ *   kdev_t dev - Device node
+ *   int *geom  - Returns geometry fields
+ *     geom[0] = heads
+ *     geom[1] = sectors
+ *     geom[2] = cylinders
+ *-------------------------------------------------------------*/
+int megaraid_biosparam(Disk *disk, kdev_t dev, int *geom)
+{
+  int                   heads, sectors, cylinders;
+  mega_host_config *megaCfg;
+
+  /* Get pointer to host config structure */
+  megaCfg = (mega_host_config *)disk->device->host->hostdata;
+
+  /* Default heads (64) & sectors (32) */
+  heads     = 64;
+  sectors   = 32;
+  cylinders = disk->capacity / (heads * sectors);
+
+  /* Handle extended translation size for logical drives > 1Gb */
+  if (disk->capacity >= 0x200000) {
+    heads     = 255;
+    sectors   = 63;
+    cylinders = disk->capacity / (heads * sectors);
+  }
+
+  /* return result */
+  geom[0] = heads;
+  geom[1] = sectors;
+  geom[2] = cylinders;
+
+  return 0;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = MEGARAID;
+
+#include "scsi_module.c"
+#endif
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/megaraid.h linux/drivers/scsi/megaraid.h
--- v2.0.35/linux/drivers/scsi/megaraid.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/megaraid.h	Sun Nov 15 10:33:09 1998
@@ -0,0 +1,282 @@
+#ifndef __MEGARAID_H__
+#define __MEGARAID_H__
+
+#define IN_ISR                  0x80000000L
+#define NO_INTR                 0x40000000L
+#define IN_TIMEOUT              0x20000000L
+#define PENDING                 0x10000000L
+#define BOARD_QUARTZ            0x08000000L
+
+#define SCB_ACTIVE 0x1
+#define SCB_WAITQ  0x2
+#define SCB_ISSUED 0x4
+
+#define SCB_FREE                -1
+#define SCB_RESET               -2
+#define SCB_ABORT               -3
+#define SCB_LOCKED              -4
+
+#define MEGA_CMD_TIMEOUT        10
+
+#define MAX_SGLIST              20
+#define MAX_COMMANDS            254
+
+#define MAX_LOGICAL_DRIVES      8
+#define MAX_CHANNEL             5
+#define MAX_TARGET              15
+#define MAX_PHYSICAL_DRIVES     MAX_CHANNEL*MAX_TARGET
+
+#define INQUIRY_DATA_SIZE       0x24
+#define MAX_CDB_LEN             0x0A
+#define MAX_REQ_SENSE_LEN       0x20
+
+#define INTR_VALID              0x40
+
+/* Mailbox commands */
+#define MEGA_MBOXCMD_LREAD      0x01
+#define MEGA_MBOXCMD_LWRITE     0x02
+#define MEGA_MBOXCMD_PASSTHRU   0x03
+#define MEGA_MBOXCMD_ADAPTERINQ 0x05
+
+/* Offsets into Mailbox */
+#define COMMAND_PORT       0x00
+#define COMMAND_ID_PORT    0x01
+#define SG_LIST_PORT0      0x08
+#define SG_LIST_PORT1      0x09
+#define SG_LIST_PORT2      0x0a
+#define SG_LIST_PORT3      0x0b
+#define SG_ELEMENT_PORT    0x0d
+#define NO_FIRED_PORT      0x0f
+
+/* I/O Port offsets */
+#define I_CMD_PORT         0x00
+#define I_ACK_PORT         0x00
+#define I_TOGGLE_PORT      0x01
+#define INTR_PORT          0x0a
+
+#define MAILBOX_SIZE       70
+#define MBOX_BUSY_PORT     0x00
+#define MBOX_PORT0         0x04
+#define MBOX_PORT1         0x05
+#define MBOX_PORT2         0x06
+#define MBOX_PORT3         0x07
+#define ENABLE_MBOX_REGION 0x0B
+
+/* I/O Port Values */
+#define ISSUE_BYTE         0x10
+#define ACK_BYTE           0x08
+#define ENABLE_INTR_BYTE   0xc0
+#define DISABLE_INTR_BYTE  0x00
+#define VALID_INTR_BYTE    0x40
+#define MBOX_BUSY_BYTE     0x10
+#define ENABLE_MBOX_BYTE   0x00
+
+/* Setup some port macros here */
+#define WRITE_MAILBOX(base,offset,value)   *(base+offset)=value
+#define READ_MAILBOX(base,offset)          *(base+offset)
+
+#define WRITE_PORT(base,offset,value)      outb_p(value,base+offset)
+#define READ_PORT(base,offset)             inb_p(base+offset)
+
+#define ISSUE_COMMAND(base)   WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE)
+#define CLEAR_INTR(base)      WRITE_PORT(base,I_ACK_PORT,ACK_BYTE)
+#define ENABLE_INTR(base)     WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE)
+#define DISABLE_INTR(base)    WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE)
+
+/* Define AMI's PCI codes */
+#undef PCI_VENDOR_ID_AMI
+#undef PCI_DEVICE_ID_AMI_MEGARAID
+
+#ifndef PCI_VENDOR_ID_AMI
+#define PCI_VENDOR_ID_AMI          0x101E
+#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
+#endif
+
+#define PCI_CONF_BASE_ADDR_OFFSET  0x10
+#define PCI_CONF_IRQ_OFFSET        0x3c
+
+#if LINUX_VERSION_CODE < 0x20100
+#define MEGARAID \
+  { NULL,                               /* Next                      */\
+    NULL,                               /* Usage Count Pointer       */\
+    NULL,                               /* /proc Directory Entry     */\
+    megaraid_proc_info,                 /* /proc Info Function       */\
+    "MegaRAID",                         /* Driver Name               */\
+    megaraid_detect,                    /* Detect Host Adapter       */\
+    megaraid_release,                   /* Release Host Adapter      */\
+    megaraid_info,                      /* Driver Info Function      */\
+    megaraid_command,                   /* Command Function          */\
+    megaraid_queue,                     /* Queue Command Function    */\
+    megaraid_abort,                     /* Abort Command Function    */\
+    megaraid_reset,                     /* Reset Command Function    */\
+    NULL,                               /* Slave Attach Function     */\
+    megaraid_biosparam,                 /* Disk BIOS Parameters      */\
+    1,                                  /* # of cmds that can be\
+                                           outstanding at any time */\
+    7,                                  /* HBA Target ID             */\
+    MAX_SGLIST,                         /* Scatter/Gather Table Size */\
+    1,                                  /* SCSI Commands per LUN     */\
+    0,                                  /* Present                   */\
+    0,                                  /* Default Unchecked ISA DMA */\
+    ENABLE_CLUSTERING }                 /* Enable Clustering         */
+#else
+#define MEGARAID \
+  {\
+    name:            "MegaRAID",               /* Driver Name               */\
+    proc_info:        megaraid_proc_info,      /* /proc driver info         */\
+    detect:           megaraid_detect,         /* Detect Host Adapter       */\
+    release:          megaraid_release,        /* Release Host Adapter      */\
+    info:             megaraid_info,           /* Driver Info Function      */\
+    command:          megaraid_command,        /* Command Function          */\
+    queuecommand:     megaraid_queue,          /* Queue Command Function    */\
+    abort:            megaraid_abort,          /* Abort Command Function    */\
+    reset:            megaraid_reset,          /* Reset Command Function    */\
+    bios_param:       megaraid_biosparam,      /* Disk BIOS Parameters      */\
+    can_queue:        255,                     /* Can Queue                 */\
+    this_id:          7,                       /* HBA Target ID             */\
+    sg_tablesize:     MAX_SGLIST,              /* Scatter/Gather Table Size */\
+    cmd_per_lun:      1,                       /* SCSI Commands per LUN     */\
+    present:          0,                       /* Present                   */\
+    unchecked_isa_dma:0,                       /* Default Unchecked ISA DMA */\
+    use_clustering:   ENABLE_CLUSTERING       /* Enable Clustering         */\
+  }
+#endif
+
+/* Structures */
+typedef struct _mega_ADP_INFO
+{
+  u_char    MaxConcCmds;
+  u_char    RbldRate;
+  u_char    MaxTargPerChan;
+  u_char    ChanPresent;
+  u_char    FwVer[4];
+  u_short   AgeOfFlash;
+  u_char    ChipSet;
+  u_char    DRAMSize;
+  u_char    CacheFlushInterval;
+  u_char    BiosVer[4];
+  u_char    resvd[7];
+} mega_ADP_INFO;
+
+typedef struct _mega_LDRV_INFO
+{
+  u_char   NumLDrv;
+  u_char   resvd[3];
+  u_long   LDrvSize[MAX_LOGICAL_DRIVES];
+  u_char   LDrvProp[MAX_LOGICAL_DRIVES];
+  u_char   LDrvState[MAX_LOGICAL_DRIVES];
+} mega_LDRV_INFO;
+
+typedef struct _mega_PDRV_INFO
+{
+  u_char   PDrvState[MAX_PHYSICAL_DRIVES];
+  u_char   resvd;
+} mega_PDRV_INFO;
+
+// RAID inquiry: Mailbox command 0x5
+typedef struct _mega_RAIDINQ
+{
+  mega_ADP_INFO    AdpInfo;
+  mega_LDRV_INFO   LogdrvInfo;
+  mega_PDRV_INFO   PhysdrvInfo;
+} mega_RAIDINQ;
+
+// Passthrough command: Mailbox command 0x3
+typedef struct mega_passthru
+{
+  u_char            timeout:3;              /* 0=6sec/1=60sec/2=10min/3=3hrs */
+  u_char            ars:1;
+  u_char            reserved:3;
+  u_char            islogical:1;
+  u_char            logdrv;                 /* if islogical == 1 */
+  u_char            channel;                /* if islogical == 0 */
+  u_char            target;                 /* if islogical == 0 */
+  u_char            queuetag;               /* unused */
+  u_char            queueaction;            /* unused */
+  u_char            cdb[MAX_CDB_LEN];
+  u_char            cdblen;
+  u_char            reqsenselen;
+  u_char            reqsensearea[MAX_REQ_SENSE_LEN];
+  u_char            numsgelements;
+  u_char            scsistatus;
+  u_long            dataxferaddr;
+  u_long            dataxferlen;
+} mega_passthru;
+
+typedef struct _mega_mailbox
+{
+  /* 0x0 */ u_char    cmd;
+  /* 0x1 */ u_char    cmdid;
+  /* 0x2 */ u_short   numsectors;
+  /* 0x4 */ u_long    lba;
+  /* 0x8 */ u_long    xferaddr;
+  /* 0xC */ u_char    logdrv;
+  /* 0xD */ u_char    numsgelements;
+  /* 0xE */ u_char    resvd;
+  /* 0xF */ u_char    busy;
+  /* 0x10*/ u_char    numstatus;
+  /* 0x11*/ u_char    status;
+  /* 0x12*/ u_char    completed[46];
+            u_char    mraid_poll;
+            u_char    mraid_ack;
+            u_char    pad[16];
+} mega_mailbox;
+
+typedef struct _mega_sglist
+{
+  u_long     address;
+  u_long     length;
+} mega_sglist;
+
+/* Queued command data */
+typedef struct _mega_scb mega_scb;
+
+struct _mega_scb
+{
+  int             idx;
+  u_long          flag;
+  Scsi_Cmnd      *SCpnt;
+  u_char          mboxData[16];
+  mega_passthru   pthru;
+  mega_sglist    *sgList;
+  mega_scb       *next;
+};
+
+/* Per-controller data */
+typedef struct _mega_host_config
+{
+  u_char               numldrv;
+  u_long               flag;
+  u_long               base;
+
+  struct tq_struct     megaTq;
+
+  /* Host adapter parameters */
+  u_char               fwVer[7];
+  u_char               biosVer[7];
+
+  struct Scsi_Host     *host;
+
+  /* The following must be DMA-able!! */
+  volatile mega_mailbox *mbox;
+  volatile mega_mailbox mailbox;
+  volatile u_char       mega_buffer[2*1024L];
+
+  u_char                max_cmds;
+  mega_scb              scbList[MAX_COMMANDS];
+} mega_host_config;
+
+extern struct proc_dir_entry proc_scsi_megaraid;
+
+const char *megaraid_info( struct Scsi_Host * );
+int        megaraid_detect( Scsi_Host_Template * );
+int        megaraid_release(struct Scsi_Host *);
+int        megaraid_command( Scsi_Cmnd * );
+int        megaraid_abort( Scsi_Cmnd * );
+int        megaraid_reset( Scsi_Cmnd *, unsigned int); 
+int        megaraid_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
+int        megaraid_biosparam( Disk *, kdev_t, int * );
+int        megaraid_proc_info( char *buffer, char **start, off_t offset,
+			       int length, int hostno, int inout );
+
+#endif
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.0.35/linux/drivers/scsi/ncr53c8xx.c	Sun Nov 15 10:49:45 1998
+++ linux/drivers/scsi/ncr53c8xx.c	Sun Nov 15 10:33:09 1998
@@ -7655,7 +7655,7 @@
 **	field of the controller's struct ncb.
 **
 **	Possible cases:		   hs  sir   msg_in value  send   goto
-**	We try try to negotiate:
+**	We try to negotiate:
 **	-> target doesnt't msgin   NEG FAIL  noop   defa.  -      dispatch
 **	-> target rejected our msg NEG FAIL  reject defa.  -      dispatch
 **	-> target answered  (ok)   NEG SYNC  sdtr   set    -      clrack
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h
--- v2.0.35/linux/drivers/scsi/ppa.h	Sun Nov 15 10:49:45 1998
+++ linux/drivers/scsi/ppa.h	Sun Nov 15 10:33:09 1998
@@ -7,6 +7,7 @@
  *	All comments to David.
  */
 
+#include <linux/config.h> /* CONFIG_SCSI_PPA_HAVE_PEDANTIC */
 #ifndef _PPA_H
 #define _PPA_H
 
@@ -54,12 +55,12 @@
  *
  * Fixed all problems in the parport sharing scheme. Now ppa can be safe
  * used with lp or other parport devices on the same parallel port.
- *		1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ *		1997 by Andrea Arcangeli
  *							[1.39]
  *
  * Little fix in ppa engine to ensure that ppa don' t release parport
  * or disconnect in wrong cases.
- *		1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ *		1997 by Andrea Arcangeli
  *							[1.40]
  *
  * Corrected ppa.h for 2.1.x kernels (>=2.1.85)
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.0.35/linux/drivers/scsi/scsi.c	Sun Nov 15 10:49:45 1998
+++ linux/drivers/scsi/scsi.c	Sun Nov 15 10:33:10 1998
@@ -284,6 +284,7 @@
 {"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"REGAL","CDC-4X","*", BLIST_MAX5LUN | BLIST_SINGLELUN},
 {"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"NAKAMICH","MJ-5.16S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
@@ -687,6 +688,7 @@
   case TYPE_MOD:
   case TYPE_PROCESSOR:
   case TYPE_SCANNER:
+  case TYPE_MEDIUM_CHANGER:
     SDpnt->writeable = 1;
     break;
   case TYPE_WORM:
@@ -750,6 +752,13 @@
     SDpnt->borken = 0;
 
   /*
+   * If we want to only allow I/O to one of the luns attached to this device
+   * at a time, then we set this flag.
+   */
+  if (bflags & BLIST_SINGLELUN)
+    SDpnt->single_lun = 1;
+
+  /*
    * These devices need this "key" to unlock the devices so we can use it
    */
   if ((bflags & BLIST_KEY) != 0) {
@@ -790,13 +799,6 @@
    */
   if (bflags & BLIST_NOLUN)
     return 0;                   /* break; */
-
-  /*
-   * If we want to only allow I/O to one of the luns attached to this device
-   * at a time, then we set this flag.
-   */
-  if (bflags & BLIST_SINGLELUN)
-    SDpnt->single_lun = 1;
 
   /*
    * If this device is known to support sparse multiple units, override the
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c
--- v2.0.35/linux/drivers/scsi/scsicam.c	Thu Nov  7 01:25:21 1996
+++ linux/drivers/scsi/scsicam.c	Sun Nov 15 10:33:10 1998
@@ -50,6 +50,7 @@
     struct buffer_head *bh;
     int ret_code;
     int size = disk->capacity;
+    unsigned long temp_cyl;
 
     if (!(bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024)))
 	return -1;
@@ -72,6 +73,11 @@
     if (ret_code || ip[0] > 255 || ip[1] > 63) {
 	 ip[0] = 64;
 	 ip[1] = 32;
+	 temp_cyl = size / (ip[0] * ip[1]);
+	 if (temp_cyl > 65534) {
+	      ip[0] = 255;
+	      ip[1] = 63;
+	 }
 	 ip[2] = size / (ip[0] * ip[1]);
     }
 
@@ -118,6 +124,8 @@
     	end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
     	end_head = largest->end_head;
     	end_sector = largest->end_sector & 0x3f;
+
+        if( end_head + 1 == 0 || end_sector == 0 ) return -1;
 
 #ifdef DEBUG
 	printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.0.35/linux/drivers/scsi/sd.c	Sun Nov 15 10:49:45 1998
+++ linux/drivers/scsi/sd.c	Sun Nov 15 10:33:10 1998
@@ -424,8 +424,8 @@
 	/* If we had an ILLEGAL REQUEST returned, then we may have
 	 * performed an unsupported command.  The only thing this should be 
 	 * would be a ten byte read where only a six byte read was supported.
-	 * Also, on a system where READ CAPACITY failed, we have have read 
-	 * past the end of the disk. 
+	 * Also, on a system where READ CAPACITY failed, we have read past
+	 * the end of the disk. 
 	 */
 
 	if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v2.0.35/linux/drivers/scsi/seagate.c	Mon May 27 03:37:18 1996
+++ linux/drivers/scsi/seagate.c	Sun Nov 15 10:33:10 1998
@@ -689,7 +689,7 @@
 		return DID_BAD_TARGET;
 
 /*
- *	We work it differently depending on if this is is "the first time,"
+ *	We work it differently depending on if this is "the first time,"
  *	or a reconnect.  If this is a reselect phase, then SEL will 
  *	be asserted, and we must skip selection / arbitration phases.
  */
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v2.0.35/linux/drivers/scsi/u14-34f.c	Mon Jul 13 13:46:36 1998
+++ linux/drivers/scsi/u14-34f.c	Sun Nov 15 10:33:10 1998
@@ -1,6 +1,18 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *      26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
+ *          Added command line option (et:[y|n]) to use the existing
+ *          translation (returned by scsicam_bios_param) as disk geometry.
+ *          The default is et:n, which uses the disk geometry jumpered
+ *          on the board.
+ *          The default value et:n is compatible with all previous revisions
+ *          of this driver.
+ *
+ *      28 May 1998 Rev. 4.32 for linux 2.0.33 and 2.1.104
+ *          Increased busy timeout from 10 msec. to 200 msec. while
+ *          processing interrupts.
+ *
  *      18 May 1998 Rev. 4.31 for linux 2.0.33 and 2.1.102
  *          Improved abort handling during the eh recovery process.
  *
@@ -9,7 +21,7 @@
  *          abort and reset routines.
  *          Added command line options (eh:[y|n]) to choose between
  *          new_eh_code and the old scsi code.
- *          If linux verion >= 2.1.101 the default is eh:y, while the eh
+ *          If linux version >= 2.1.101 the default is eh:y, while the eh
  *          option is ignored for previous releases and the old scsi code
  *          is used.
  *
@@ -241,20 +253,23 @@
  *
  *  eh:y  use new scsi code (linux 2.2 only);
  *  eh:n  use old scsi code;
+ *  et:y  use disk geometry returned by scsicam_bios_param;
+ *  et:n  use disk geometry jumpered on the board;
  *  lc:y  enables linked commands;
  *  lc:n  disables linked commands;
  *  of:y  enables old firmware support;
  *  of:n  disables old firmware support;
  *  mq:xx set the max queue depth to the value xx (2 <= xx <= 8).
  *
- *  The default value is: "u14-34f=lc:n,of:n,mq:8". An example using the list
- *  of detection probes could be: "u14-34f=0x230,0x340,lc:y,of:n,mq:4,eh:n".
+ *  The default value is: "u14-34f=lc:n,of:n,mq:8,et:n".
+ *  An example using the list of detection probes could be:
+ *  "u14-34f=0x230,0x340,lc:y,of:n,mq:4,eh:n,et:n".
  *
  *  When loading as a module, parameters can be specified as well.
  *  The above example would be (use 1 in place of y and 0 in place of n):
  *
  *  modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \
- *                max_queue_depth=4 use_new_eh_code=0
+ *                max_queue_depth=4 use_new_eh_code=0 ext_tran=0
  *
  *  ----------------------------------------------------------------------------
  *  In this implementation, linked commands are designed to work with any DISK
@@ -310,6 +325,7 @@
 MODULE_PARM(link_statistics, "i");
 MODULE_PARM(max_queue_depth, "i");
 MODULE_PARM(use_new_eh_code, "i");
+MODULE_PARM(ext_tran, "i");
 MODULE_AUTHOR("Dario Ballabio");
 #endif
 
@@ -401,6 +417,7 @@
 #undef  DEBUG_RESET
 #undef  DEBUG_GENERATE_ERRORS
 #undef  DEBUG_GENERATE_ABORTS
+#undef  DEBUG_GEOMETRY
 
 #define MAX_ISA 3
 #define MAX_VESA 1
@@ -552,6 +569,7 @@
 static int do_trace = FALSE;
 static int setup_done = FALSE;
 static int link_statistics = 0;
+static int ext_tran = FALSE;
 
 #if defined(HAVE_OLD_UX4F_FIRMWARE)
 static int have_old_firmware = TRUE;
@@ -872,9 +890,9 @@
 
    if (j == 0) {
       printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n");
-      printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c.\n",
+      printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c, et:%c.\n",
              driver_name, YESNO(have_old_firmware), YESNO(linked_comm),
-             max_queue_depth, YESNO(use_new_eh_code));
+             max_queue_depth, YESNO(use_new_eh_code), YESNO(ext_tran));
       }
 
    printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d.\n",
@@ -918,6 +936,7 @@
       else if (!strncmp(cur, "mq:", 3))  max_queue_depth = val;
       else if (!strncmp(cur, "ls:", 3))  link_statistics = val;
       else if (!strncmp(cur, "eh:", 3))  use_new_eh_code = val;
+      else if (!strncmp(cur, "et:", 3))  ext_tran = val;
 
       if ((cur = strchr(cur, ','))) ++cur;
       }
@@ -1551,6 +1570,18 @@
    dkinfo[0] = HD(j)->heads;
    dkinfo[1] = HD(j)->sectors;
    dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors);
+
+   if (ext_tran && (scsicam_bios_param(disk, dev, dkinfo) < 0)) {
+      dkinfo[0] = 255;
+      dkinfo[1] = 63;
+      dkinfo[2] = size / (dkinfo[0] * dkinfo[1]);
+      }
+
+#if defined (DEBUG_GEOMETRY)
+   printk ("%s: biosparam, head=%d, sec=%d, cyl=%d.\n", driver_name,
+           dkinfo[0], dkinfo[1], dkinfo[2]);
+#endif
+
    return FALSE;
 }
 
@@ -1733,7 +1764,7 @@
                         HD(j)->iocount);
 
    /* Check if this board is still busy */
-   if (wait_on_busy(sh[j]->io_port, MAXLOOP)) {
+   if (wait_on_busy(sh[j]->io_port, 20 * MAXLOOP)) {
       outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
       printk("%s: ihdlr, busy timeout error,  irq %d, reg 0x%x, count %d.\n",
              BN(j), irq, reg, HD(j)->iocount);
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v2.0.35/linux/drivers/scsi/u14-34f.h	Mon Jul 13 13:46:36 1998
+++ linux/drivers/scsi/u14-34f.h	Sun Nov 15 10:33:10 1998
@@ -4,6 +4,7 @@
 #ifndef _U14_34F_H
 #define _U14_34F_H
 
+#include <scsi/scsicam.h>
 #include <linux/version.h>
 
 int u14_34f_detect(Scsi_Host_Template *);
@@ -15,7 +16,7 @@
 int u14_34f_old_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "4.31.00"
+#define U14_34F_VERSION "4.33.00"
 
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
 
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.0.35/linux/drivers/scsi/wd7000.c	Sun Nov 15 10:49:45 1998
+++ linux/drivers/scsi/wd7000.c	Sun Nov 15 10:33:10 1998
@@ -102,44 +102,56 @@
  *
  *  Revisions by Miroslav Zagorac <zaga@fly.cc.fer.hr>
  *
- *  08/24/1996.
+ * -- 08/24/1996. --------------------------------------------------------------
+ *    Enhancement for wd7000_detect function has been made, so you don't have
+ *    to enter BIOS ROM address in initialisation data (see struct Config).
+ *    We cannot detect IRQ, DMA and I/O base address for now, so we have to
+ *    enter them as arguments while wd_7000 is detected.  If someone has IRQ,
+ *    DMA or an I/O base address set to some other value, he can enter them in
+ *    a configuration without any problem.
+ *    Also I wrote a function wd7000_setup, so now you can enter WD-7000
+ *    definition as kernel arguments, as in lilo.conf:
+ *
+ *       append="wd7000=IRQ,DMA,IO"
+ *
+ *   PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize
+ *       adapter, unlike the old one.  Anyway, BIOS ROM from WD7000 adapter is
+ *       useless for Linux. B^)
+ *
+ * -- 09/06/1996. --------------------------------------------------------------
+ *    Auto detecting of an I/O base address from wd7000_detect function is
+ *    removed, some little bugs too...
+ *
+ *    Thanks to Roger Scott for driver debugging.
+ *
+ * -- 06/07/1997. --------------------------------------------------------------
+ *    Added support for /proc file system (/proc/scsi/wd7000/[0...] files).
+ *    Now, the driver can handle hard disks with capacity >1GB.
+ *
+ * -- 01/15/1998. --------------------------------------------------------------
+ *    Added support for BUS_ON and BUS_OFF parameters in config line.
+ *    Miscellaneous cleanups.  Syntax of the append line is changed to:
+ *
+ *       append="wd7000=IRQ,DMA,IO[,BUS_ON[,BUS_OFF]]"
+ *
+ *    , where BUS_ON and BUS_OFF are time in nanoseconds.
+ *
+ * -- 03/01/1998. --------------------------------------------------------------
+ *    The WD7000 driver now works on kernels' >= 2.1.x
+ *
+ * -- 06/11/1998. --------------------------------------------------------------
+ *    Ugly init_scbs, alloc_scbs and free_scb functions are changed with
+ *    scbs_init, scb_alloc and scb_free.  Now, source code is identical on
+ *    2.0.xx and 2.1.xx kernels.
+ *    WD7000 specific definitions are moved from this file to wd7000.h.
  *
- *  Enhancement for wd7000_detect function has been made, so you don't have
- *  to enter BIOS ROM adress in initialisation data (see struct Config).
- *  We cannot detect IRQ, DMA and I/O base address for now, so we have to
- *  enter them as arguments while wd_7000 is detected. If someone has IRQ,
- *  DMA or I/O base address set to some other value, he can enter them in
- *  configuration without any problem. Also I wrote a function wd7000_setup,
- *  so now you can enter WD-7000 definition as kernel arguments,
- *  as in lilo.conf:
- *
- *     append="wd7000=IRQ,DMA,IO"
- *
- *  PS: If card BIOS ROM is disabled, function wd7000_detect now will recognize
- *      adapter, unlike the old one. Anyway, BIOS ROM from WD7000 adapter is
- *      useless for Linux. B^)
- *
- *
- *  09/06/1996.
- *
- *  Autodetecting of I/O base address from wd7000_detect function is removed,
- *  some little bugs removed, etc...
- *
- *  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.
- *
- *  01/15/1998
- *
- *  Added support for BUS_ON and BUS_OFF parameters in config line.
- *  Miscellaneous cleanup.
  */
-
 #ifdef MODULE
-#include <linux/module.h>
+#  include <linux/module.h>
+#endif
+
+#if (LINUX_VERSION_CODE >= 0x020100)
+#  include <asm/spinlock.h>
 #endif
 
 #include <stdarg.h>
@@ -155,16 +167,17 @@
 #include <linux/ioport.h>
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
+#include <linux/version.h>
+#include <linux/stat.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
 #include <scsi/scsicam.h>
 
-#define ANY2SCSI_INLINE		/* undef this to use old macros */
-#undef WD7000_DEBUG		/* general debug                */
+#undef WD7000_DEBUG	/* general debug         */
+#define WD7000_DEFINES	/* This must be defined! */
 
 #include "wd7000.h"
-#include <linux/stat.h>
 
 
 struct proc_dir_entry proc_scsi_wd7000 =
@@ -176,86 +189,28 @@
     2
 };
 
-
-/*
- *  Mailbox structure sizes.
- *  I prefer to keep the number of ICMBs much larger than the number of
- *  OGMBs.  OGMBs are used very quickly by the driver to start one or
- *  more commands, while ICMBs are used by the host adapter per command.
- */
-#define OGMB_CNT	16
-#define ICMB_CNT	32
-
-/*
- *  Scb's are shared by all active adapters.  So, if they all become busy,
- *  callers may be made to wait in alloc_scbs for them to free.  That can
- *  be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q.  If you'd
- *  rather conserve memory, use a smaller number (> 0, of course) - things
- *  will should still work OK.
- */
-#define MAX_SCBS        32
-
-/*
- *  WD7000-specific mailbox structure
- *
- */
-typedef volatile struct mailbox {
-    unchar status;
-    unchar scbptr[3];		/* SCSI-style - MSB first (big endian) */
-} Mailbox;
-
-/*
- *  This structure should contain all per-adapter global data.  I.e., any
- *  new global per-adapter data should put in here.
- */
-typedef struct adapter {
-    struct Scsi_Host *sh;	/* Pointer to Scsi_Host structure    */
-    int iobase;			/* This adapter's I/O base address   */
-    int irq;			/* This adapter's IRQ level          */
-    int dma;			/* This adapter's DMA channel        */
-    int int_counter;		/* This adapter's interrupt counter  */
-    int bus_on;			/* This adapter's BUS_ON time        */
-    int bus_off;		/* This adapter's BUS_OFF time       */
-    struct {			/* This adapter's mailboxes          */
-	Mailbox ogmb[OGMB_CNT];	/* Outgoing mailboxes                */
-	Mailbox icmb[ICMB_CNT];	/* Incoming mailboxes                */
-    } mb;
-    int next_ogmb;		/* to reduce contention at mailboxes */
-    unchar control;		/* shadows CONTROL port value        */
-    unchar rev1, rev2;		/* filled in by wd7000_revision      */
-} Adapter;
-
 /*
  * (linear) base address for ROM BIOS
  */
-static const long wd7000_biosaddr[] =
-{
+static const long wd7000_biosaddr[] = {
     0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000,
     0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000
 };
-#define NUM_ADDRS (sizeof(wd7000_biosaddr)/sizeof(long))
+#define NUM_ADDRS	(sizeof (wd7000_biosaddr) / sizeof (long))
 
-static const unsigned short wd7000_iobase[] =
-{
+static const ushort wd7000_iobase[] = {
     0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,
     0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,
     0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,
     0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8
 };
-#define NUM_IOPORTS (sizeof(wd7000_iobase)/sizeof(unsigned short))
+#define NUM_IOPORTS	(sizeof (wd7000_iobase) / sizeof (ushort))
 
 static const short wd7000_irq[] = { 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 };
-#define NUM_IRQS (sizeof(wd7000_irq)/sizeof(short))
+#define NUM_IRQS	(sizeof (wd7000_irq) / sizeof (short))
 
 static const short wd7000_dma[] = { 5, 6, 7 };
-#define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short))
-
-/*
- * possible irq range
- */
-#define IRQ_MIN   3
-#define IRQ_MAX   15
-#define IRQS      (IRQ_MAX - IRQ_MIN + 1)
+#define NUM_DMAS	(sizeof (wd7000_dma) / sizeof (short))
 
 /*
  * The following is set up by wd7000_detect, and used thereafter by
@@ -265,23 +220,6 @@
  */
 static struct Scsi_Host *wd7000_host[IRQS];
 
-#define BUS_ON    64	/* x 125ns = 8000ns (BIOS default) */
-#define BUS_OFF   15	/* x 125ns = 1875ns (BIOS default) */
-
-/*
- *  Standard Adapter Configurations - used by wd7000_detect
- */
-typedef struct {
-    short irq;		/* IRQ level                                  */
-    short dma;		/* DMA channel                                */
-    unsigned iobase;	/* I/O base address                           */
-    short bus_on;	/* Time that WD7000 spends on the AT-bus when */
-			/* transferring data. BIOS default is 8000ns. */
-    short bus_off;	/* Time that WD7000 spends OFF THE BUS after  */
-			/* while it is transferring data.             */
-			/* BIOS default is 1875ns                     */
-} Config;
-
 /*
  * Add here your configuration...
  */
@@ -294,276 +232,12 @@
 };
 #define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
 
-/*
- *  The following list defines strings to look for in the BIOS that identify
- *  it as the WD7000-FASST2 SST BIOS.  I suspect that something should be
- *  added for the Future Domain version.
- */
-typedef struct signature {
-    const void *sig;		/* String to look for            */
-    unsigned ofs;		/* offset from BIOS base address */
-    unsigned len;		/* length of string              */
-} Signature;
-
 static const Signature signatures[] =
 {
     {"SSTBIOS", 0x0000d, 7}	/* "SSTBIOS" @ offset 0x0000d */
 };
 #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
 
-
-/*
- *  I/O Port Offsets and Bit Definitions
- *  4 addresses are used.  Those not defined here are reserved.
- */
-#define ASC_STAT        0	/* Status,  Read          */
-#define ASC_COMMAND     0	/* Command, Write         */
-#define ASC_INTR_STAT   1	/* Interrupt Status, Read */
-#define ASC_INTR_ACK    1	/* Acknowledge, Write     */
-#define ASC_CONTROL     2	/* Control, Write         */
-
-/*
- * ASC Status Port
- */
-#define INT_IM		0x80	/* Interrupt Image Flag           */
-#define CMD_RDY		0x40	/* Command Port Ready             */
-#define CMD_REJ		0x20	/* Command Port Byte Rejected     */
-#define ASC_INIT        0x10	/* ASC Initialized Flag           */
-#define ASC_STATMASK    0xf0	/* The lower 4 Bytes are reserved */
-
-/*
- * COMMAND opcodes
- *
- *  Unfortunately, I have no idea how to properly use some of these commands,
- *  as the OEM manual does not make it clear.  I have not been able to use
- *  enable/disable unsolicited interrupts or the reset commands with any
- *  discernible effect whatsoever.  I think they may be related to certain
- *  ICB commands, but again, the OEM manual doesn't make that clear.
- */
-#define NO_OP             0	/* NO-OP toggles CMD_RDY bit in ASC_STAT  */
-#define INITIALIZATION    1	/* initialization (10 bytes)              */
-#define DISABLE_UNS_INTR  2	/* disable unsolicited interrupts         */
-#define ENABLE_UNS_INTR   3	/* enable unsolicited interrupts          */
-#define INTR_ON_FREE_OGMB 4	/* interrupt on free OGMB                 */
-#define SOFT_RESET        5	/* SCSI bus soft reset                    */
-#define HARD_RESET_ACK    6	/* SCSI bus hard reset acknowledge        */
-#define START_OGMB        0x80	/* start command in OGMB (n)              */
-#define SCAN_OGMBS        0xc0	/* start multiple commands, signature (n) */
-				/*    where (n) = lower 6 bits            */
-/*
- * For INITIALIZATION:
- */
-typedef struct initCmd {
-    unchar op;			/* command opcode (= 1)                    */
-    unchar ID;			/* Adapter's SCSI ID                       */
-    unchar bus_on;		/* Bus on time, x 125ns (see below)        */
-    unchar bus_off;		/* Bus off time, ""         ""             */
-    unchar rsvd;		/* Reserved                                */
-    unchar mailboxes[3];	/* Address of Mailboxes, MSB first         */
-    unchar ogmbs;		/* Number of outgoing MBs, max 64, 0,1 = 1 */
-    unchar icmbs;		/* Number of incoming MBs,   ""       ""   */
-} InitCmd;
-
-/*
- * Interrupt Status Port - also returns diagnostic codes at ASC reset
- *
- * if msb is zero, the lower bits are diagnostic status
- * Diagnostics:
- * 01   No diagnostic error occurred
- * 02   RAM failure
- * 03   FIFO R/W failed
- * 04   SBIC register read/write failed
- * 05   Initialization D-FF failed
- * 06   Host IRQ D-FF failed
- * 07   ROM checksum error
- * Interrupt status (bitwise):
- * 10NNNNNN   outgoing mailbox NNNNNN is free
- * 11NNNNNN   incoming mailbox NNNNNN needs service
- */
-#define MB_INTR    0xC0		/* Mailbox Service possible/required */
-#define IMB_INTR   0x40		/* 1 Incoming / 0 Outgoing           */
-#define MB_MASK    0x3f		/* mask for mailbox number           */
-
-/*
- * CONTROL port bits
- */
-#define INT_EN     0x08		/* Interrupt Enable */
-#define DMA_EN     0x04		/* DMA Enable       */
-#define SCSI_RES   0x02		/* SCSI Reset       */
-#define ASC_RES    0x01		/* ASC Reset        */
-
-/*
- * Driver data structures:
- *   - mb and scbs are required for interfacing with the host adapter.
- *     An SCB has extra fields not visible to the adapter; mb's
- *     _cannot_ do this, since the adapter assumes they are contiguous in
- *     memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
- *     to access them.
- *   - An icb is for host-only (non-SCSI) commands.  ICBs are 16 bytes each;
- *     the additional bytes are used only by the driver.
- *   - For now, a pool of SCBs are kept in global storage by this driver,
- *     and are allocated and freed as needed.
- *
- *  The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
- *  not when it has finished.  Since the SCB must be around for completion,
- *  problems arise when SCBs correspond to OGMBs, which may be reallocated
- *  earlier (or delayed unnecessarily until a command completes).
- *  Mailboxes are used as transient data structures, simply for
- *  carrying SCB addresses to/from the 7000-FASST2.
- *
- *  Note also since SCBs are not "permanently" associated with mailboxes,
- *  there is no need to keep a global list of Scsi_Cmnd pointers indexed
- *  by OGMB.   Again, SCBs reference their Scsi_Cmnds directly, so mailbox
- *  indices need not be involved.
- */
-
-/*
- *  WD7000-specific scatter/gather element structure
- */
-typedef struct sgb {
-    unchar len[3];
-    unchar ptr[3];		/* Also SCSI-style - MSB first */
-} Sgb;
-
-typedef struct scb {		/* Command Control Block 5.4.1               */
-    unchar op;			/* Command Control Block Operation Code      */
-    unchar idlun;		/* op=0,2:Target Id, op=1:Initiator Id       */
-				/* Outbound data transfer, length is checked */
-				/* Inbound data transfer, length is checked  */
-				/* Logical Unit Number                       */
-    unchar cdb[12];		/* SCSI Command Block                        */
-    volatile unchar status;	/* SCSI Return Status                        */
-    volatile unchar vue;	/* Vendor Unique Error Code                  */
-    unchar maxlen[3];		/* Maximum Data Transfer Length              */
-    unchar dataptr[3];		/* SCSI Data Block Pointer                   */
-    unchar linkptr[3];		/* Next Command Link Pointer                 */
-    unchar direc;		/* Transfer Direction                        */
-    unchar reserved2[6];	/* SCSI Command Descriptor Block             */
-				/* end of hardware SCB                       */
-    Scsi_Cmnd *SCpnt;		/* Scsi_Cmnd using this SCB                  */
-    Sgb sgb[WD7000_SG];		/* Scatter/gather list for this SCB          */
-    Adapter *host;		/* host adapter                              */
-    struct scb *next;		/* for lists of scbs                         */
-} Scb;
-
-/*
- *  This driver is written to allow host-only commands to be executed.
- *  These use a 16-byte block called an ICB.  The format is extended by the
- *  driver to 18 bytes, to support the status returned in the ICMB and
- *  an execution phase code.
- *
- *  There are other formats besides these; these are the ones I've tried
- *  to use.  Formats for some of the defined ICB opcodes are not defined
- *  (notably, get/set unsolicited interrupt status) in my copy of the OEM
- *  manual, and others are ambiguous/hard to follow.
- */
-#define ICB_OP_MASK           0x80	/* distinguishes scbs from icbs        */
-#define ICB_OP_OPEN_RBUF      0x80	/* open receive buffer                 */
-#define ICB_OP_RECV_CMD       0x81	/* receive command from initiator      */
-#define ICB_OP_RECV_DATA      0x82	/* receive data from initiator         */
-#define ICB_OP_RECV_SDATA     0x83	/* receive data with status from init. */
-#define ICB_OP_SEND_DATA      0x84	/* send data with status to initiator  */
-#define ICB_OP_SEND_STAT      0x86	/* send command status to initiator    */
-					/* 0x87 is reserved                    */
-#define ICB_OP_READ_INIT      0x88	/* read initialization bytes           */
-#define ICB_OP_READ_ID        0x89	/* read adapter's SCSI ID              */
-#define ICB_OP_SET_UMASK      0x8A	/* set unsolicited interrupt mask      */
-#define ICB_OP_GET_UMASK      0x8B	/* read unsolicited interrupt mask     */
-#define ICB_OP_GET_REVISION   0x8C	/* read firmware revision level        */
-#define ICB_OP_DIAGNOSTICS    0x8D	/* execute diagnostics                 */
-#define ICB_OP_SET_EPARMS     0x8E	/* set execution parameters            */
-#define ICB_OP_GET_EPARMS     0x8F	/* read execution parameters           */
-
-typedef struct icbRecvCmd {
-    unchar op;
-    unchar IDlun;		/* Initiator SCSI ID/lun     */
-    unchar len[3];		/* command buffer length     */
-    unchar ptr[3];		/* command buffer address    */
-    unchar rsvd[7];		/* reserved                  */
-    volatile unchar vue;	/* vendor-unique error code  */
-    volatile unchar status;	/* returned (icmb) status    */
-    volatile unchar phase;	/* used by interrupt handler */
-} IcbRecvCmd;
-
-typedef struct icbSendStat {
-    unchar op;
-    unchar IDlun;		/* Target SCSI ID/lun                  */
-    unchar stat;		/* (outgoing) completion status byte 1 */
-    unchar rsvd[12];		/* reserved                            */
-    volatile unchar vue;	/* vendor-unique error code            */
-    volatile unchar status;	/* returned (icmb) status              */
-    volatile unchar phase;	/* used by interrupt handler           */
-} IcbSendStat;
-
-typedef struct icbRevLvl {
-    unchar op;
-    volatile unchar primary;	/* primary revision level (returned)   */
-    volatile unchar secondary;	/* secondary revision level (returned) */
-    unchar rsvd[12];		/* reserved                            */
-    volatile unchar vue;	/* vendor-unique error code            */
-    volatile unchar status;	/* returned (icmb) status              */
-    volatile unchar phase;	/* used by interrupt handler           */
-} IcbRevLvl;
-
-typedef struct icbUnsMask {	/* I'm totally guessing here */
-    unchar op;
-    volatile unchar mask[14];	/* mask bits                 */
-#if 0
-    unchar rsvd[12];		/* reserved                  */
-#endif
-    volatile unchar vue;	/* vendor-unique error code  */
-    volatile unchar status;	/* returned (icmb) status    */
-    volatile unchar phase;	/* used by interrupt handler */
-} IcbUnsMask;
-
-typedef struct icbDiag {
-    unchar op;
-    unchar type;		/* diagnostics type code (0-3) */
-    unchar len[3];		/* buffer length               */
-    unchar ptr[3];		/* buffer address              */
-    unchar rsvd[7];		/* reserved                    */
-    volatile unchar vue;	/* vendor-unique error code    */
-    volatile unchar status;	/* returned (icmb) status      */
-    volatile unchar phase;	/* used by interrupt handler   */
-} IcbDiag;
-
-#define ICB_DIAG_POWERUP   0	/* Power-up diags only       */
-#define ICB_DIAG_WALKING   1	/* walking 1's pattern       */
-#define ICB_DIAG_DMA       2	/* DMA - system memory diags */
-#define ICB_DIAG_FULL      3	/* do both 1 & 2             */
-
-typedef struct icbParms {
-    unchar op;
-    unchar rsvd1;		/* reserved                  */
-    unchar len[3];		/* parms buffer length       */
-    unchar ptr[3];		/* parms buffer address      */
-    unchar idx[2];		/* index (MSB-LSB)           */
-    unchar rsvd2[5];		/* reserved                  */
-    volatile unchar vue;	/* vendor-unique error code  */
-    volatile unchar status;	/* returned (icmb) status    */
-    volatile unchar phase;	/* used by interrupt handler */
-} IcbParms;
-
-typedef struct icbAny {
-    unchar op;
-    unchar data[14];		/* format-specific data      */
-    volatile unchar vue;	/* vendor-unique error code  */
-    volatile unchar status;	/* returned (icmb) status    */
-    volatile unchar phase;	/* used by interrupt handler */
-} IcbAny;
-
-typedef union icb {
-    unchar op;			/* ICB opcode                     */
-    IcbRecvCmd recv_cmd;	/* format for receive command     */
-    IcbSendStat send_stat;	/* format for send status         */
-    IcbRevLvl rev_lvl;		/* format for get revision level  */
-    IcbDiag diag;		/* format for execute diagnostics */
-    IcbParms eparms;		/* format for get/set exec parms  */
-    IcbAny icb;			/* generic format                 */
-    unchar data[18];
-} Icb;
-
-
 /*
  *  Driver SCB structure pool.
  *
@@ -571,8 +245,7 @@
  *  structure is not part of the Adapter structure.
  */
 static Scb scbs[MAX_SCBS];
-static Scb *scbfree = NULL;	/* free list         */
-static int freescbs = MAX_SCBS;	/* free list counter */
+
 
 /*
  *  END of data/declarations - code follows.
@@ -599,6 +272,7 @@
  *
  * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values
  * are 8000ns for BUS_ON and 1875ns for BUS_OFF.
+ *
  * eg:
  *     wd7000=7,6,0x350
  *
@@ -611,14 +285,15 @@
     short i, j;
 
     if (wd7000_card_num >= NUM_CONFIGS) {
-	printk ("wd7000_setup: Too many \"wd7000=\" configurations in "
-		"command line!\n");
+	printk ("%s: Too many \"wd7000=\" configurations in "
+		"command line!\n", __FUNCTION__);
 	return;
     }
 
     if ((ints[0] < 3) || (ints[0] > 5))
-	printk ("wd7000_setup: Error in command line!  "
-		"Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>[,<BUS_OFF>]]\n");
+	printk ("%s: Error in command line!  "
+		"Usage: wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]]\n",
+		__FUNCTION__);
     else {
 	for (i = 0; i < NUM_IRQS; i++)
 	    if (ints[1] == wd7000_irq[i])
@@ -660,7 +335,7 @@
 	        configs[wd7000_card_num].bus_on = BUS_ON;
 	    }
 	    else
-	        configs[wd7000_card_num].bus_on = ints[4] / 125.0;
+	        configs[wd7000_card_num].bus_on = ints[4] / 125;
 	}
 	else
 	    configs[wd7000_card_num].bus_on = BUS_ON;
@@ -672,12 +347,12 @@
 	        configs[wd7000_card_num].bus_off = BUS_OFF;
 	    }
 	    else
-	        configs[wd7000_card_num].bus_off = ints[5] / 125.0;
+	        configs[wd7000_card_num].bus_off = ints[5] / 125;
 	}
 	else
 	    configs[wd7000_card_num].bus_off = BUS_OFF;
 
-	if (wd7000_card_num)
+	if (wd7000_card_num) {
 	    for (i = 0; i < (wd7000_card_num - 1); i++)
 		for (j = i + 1; j < wd7000_card_num; j++)
 		    if (configs[i].irq == configs[j].irq) {
@@ -692,9 +367,11 @@
 	                setup_error ("duplicated I/O base address!", ints);
 			return;
 		    }
+	}
 
 #ifdef WD7000_DEBUG
-	printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n",
+	printk ("%s: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n",
+	        __FUNCTION__,
 		configs[wd7000_card_num].irq,
 		configs[wd7000_card_num].dma,
 		configs[wd7000_card_num].iobase,
@@ -707,7 +384,6 @@
 }
 
 
-#ifdef ANY2SCSI_INLINE
 /*
  * Since they're used a lot, I've redone the following from the macros
  * formerly in wd7000.h, hopefully to speed them up by getting rid of
@@ -716,45 +392,25 @@
  * xany2scsi and xscsi2int were not being used, and are no longer defined.
  * (They were simply 4-byte versions of these routines).
  */
-typedef union {			/* let's cheat... */
-    int i;
-    unchar u[sizeof (int)];	/* the sizeof(int) makes it more portable */
-} i_u;
-
-
-static inline void any2scsi (unchar * scsi, int any)
+static inline void any2scsi (unchar *scsi, int any)
 {
     *scsi++ = ((i_u) any).u[2];
     *scsi++ = ((i_u) any).u[1];
-    *scsi++ = ((i_u) any).u[0];
+    *scsi   = ((i_u) any).u[0];
 }
 
 
-static inline int scsi2int (unchar * scsi)
+static inline int scsi2int (unchar *scsi)
 {
     i_u result;
 
     result.i = 0;		/* clears unused bytes */
-    *(result.u + 2) = *scsi++;
-    *(result.u + 1) = *scsi++;
-    *(result.u) = *scsi++;
+    result.u[2] = *scsi++;
+    result.u[1] = *scsi++;
+    result.u[0] = *scsi;
 
     return (result.i);
 }
-#else
-/*
- * These are the old ones - I've just moved them here...
- */
-#undef any2scsi
-#define any2scsi(up, p)   (up)[0] = (((unsigned long) (p)) >> 16);	\
-			  (up)[1] = ((unsigned long) (p)) >> 8;		\
-			  (up)[2] = ((unsigned long) (p));
-
-#undef scsi2int
-#define scsi2int(up)   ( (((unsigned long) *(up)) << 16) +	\
-			 (((unsigned long) (up)[1]) << 8) +	\
-			 ((unsigned long) (up)[2]) )
-#endif
 
 
 static inline void wd7000_enable_intr (Adapter *host)
@@ -773,12 +429,10 @@
 }
 
 
-#define WAITnexttimeout 200	/* 2 seconds */
-
-static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned noneof)
+static inline short WAIT (uint port, uint mask, uint allof, uint noneof)
 {
-    register unsigned WAITbits;
-    register unsigned long WAITtimeout = jiffies + WAITnexttimeout;
+    register uint WAITbits;
+    register ulong WAITtimeout = jiffies + WAITnexttimeout;
 
     while (jiffies <= WAITtimeout) {
 	WAITbits = inb (port) & mask;
@@ -791,131 +445,97 @@
 }
 
 
-static inline void delay (unsigned how_long)
+static inline void delay (uint how_long)
 {
-    register unsigned long time = jiffies + how_long;
+    register ulong time = jiffies + how_long;
 
     while (jiffies < time);
 }
 
 
-static inline int command_out (Adapter * host, unchar * cmd, int len)
+static inline int wd7000_command_out (Adapter *host, unchar *cmd, int len)
 {
-    if (!WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
-	while (len--) {
+    if (! WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+	for ( ; len--; cmd++)
 	    do {
 		outb (*cmd, host->iobase + ASC_COMMAND);
 		WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
 	    } while (inb (host->iobase + ASC_STAT) & CMD_REJ);
 
-	    cmd++;
-	}
-
 	return (1);
     }
 
-    printk ("wd7000 command_out: WAIT failed(%d)\n", len + 1);
+    printk ("%s: WAIT failed (%d)\n", __FUNCTION__, len + 1);
 
     return (0);
 }
 
 
-/*
- *  This version of alloc_scbs is in preparation for supporting multiple
- *  commands per lun and command chaining, by queueing pending commands.
- *  We will need to allocate Scbs in blocks since they will wait to be
- *  executed so there is the possibility of deadlock otherwise.
- *  Also, to keep larger requests from being starved by smaller requests,
- *  we limit access to this routine with an internal busy flag, so that
- *  the satisfiability of a request is not dependent on the size of the
- *  request.
- */
-static inline Scb *alloc_scbs (int needed)
+static inline void scbs_init (void)
 {
-    register Scb *scb, *p;
-    register unsigned long flags;
-    register unsigned long timeout = jiffies + WAITnexttimeout;
-    register unsigned long now;
-    static int busy = 0;
-    int i;
-
-    if (needed <= 0)
-	return (NULL);		/* sanity check */
-
-    save_flags (flags);
-    cli ();
-    while (busy) {		/* someone else is allocating */
-	sti ();			/* Yes this is really needed here */
-	for (now = jiffies; now == jiffies; );	/* wait a jiffy */
-	cli ();
-    }
-    busy = 1;			/* not busy now; it's our turn */
-
-    while (freescbs < needed) {
-	timeout = jiffies + WAITnexttimeout;
-	do {
-	    sti ();		/* Yes this is really needed here */
-	    for (now = jiffies; now == jiffies; );	/* wait a jiffy */
-	    cli ();
-	} while (freescbs < needed && jiffies <= timeout);
-	/*
-	 *  If we get here with enough free Scbs, we can take them.
-	 *  Otherwise, we timed out and didn't get enough.
-	 */
-	if (freescbs < needed) {
-	    busy = 0;
-	    panic ("wd7000: can't get enough free SCBs.\n");
-	    restore_flags (flags);
-	    return (NULL);
-	}
-    }
-    scb = scbfree;
-    freescbs -= needed;
-    for (i = 0; i < needed; i++) {
-	p = scbfree;
-	scbfree = p->next;
-    }
-    p->next = NULL;
-    busy = 0;			/* we're done */
-
-    restore_flags (flags);
+    short i;
 
-    return (scb);
+    for (i = 0; i < MAX_SCBS; i++)
+	memset ((void *) &(scbs[i]), 0, sizeof (Scb));
 }
 
 
-static inline void free_scb (Scb *scb)
+static inline Scb *scb_alloc (void)
 {
-    register unsigned long flags;
+    Scb *scb = NULL;
+    ulong flags;
+    short i;
+#ifdef WD7000_DEBUG
+    short free_scbs = 0;
+#endif
 
     save_flags (flags);
     cli ();
 
-    memset (scb, 0, sizeof (Scb));
-    scb->next = scbfree;
-    scbfree = scb;
-    freescbs++;
+    for (i = 0; i < MAX_SCBS; i++)
+	if (! scbs[i].used) {
+	    scbs[i].used = 1;
+	    scb = &(scbs[i]);
+
+	    break;
+	}
+
+#ifdef WD7000_DEBUG
+    for (i = 0; i < MAX_SCBS; i++)
+	free_scbs += scbs[i].used ? 0 : 1;
+
+    printk ("wd7000_%s: allocating scb (0x%08x), %d scbs free\n",
+	    __FUNCTION__, (int) scb, free_scbs);
+#endif
 
     restore_flags (flags);
+
+    return (scb);
 }
 
 
-static inline void init_scbs (void)
+static inline void scb_free (Scb *scb)
 {
-    int i;
-    unsigned long flags;
+    short i;
+    ulong flags;
 
     save_flags (flags);
     cli ();
 
-    scbfree = &(scbs[0]);
-    memset (scbs, 0, sizeof (scbs));
-    for (i = 0; i < MAX_SCBS - 1; i++) {
-	scbs[i].next = &(scbs[i + 1]);
-	scbs[i].SCpnt = NULL;
-    }
-    scbs[MAX_SCBS - 1].next = NULL;
-    scbs[MAX_SCBS - 1].SCpnt = NULL;
+    for (i = 0; i < MAX_SCBS; i++)
+	if (&(scbs[i]) == scb) {
+	    memset ((void *) &(scbs[i]), 0, sizeof (Scb));
+
+	    break;
+	}
+
+    if (i == MAX_SCBS)
+	printk ("wd7000_%s: trying to free alien scb (0x%08x)...\n",
+		__FUNCTION__, (int) scb);
+#ifdef WD7000_DEBUG
+    else
+	printk ("wd7000_%s: freeing scb (0x%08x)\n", __FUNCTION__, (int) scb);
+#endif
 
     restore_flags (flags);
 }
@@ -927,18 +547,19 @@
  */
 {
     register int i, ogmb;
-    register unsigned long flags;
+    ulong flags;
     unchar start_ogmb;
     Mailbox *ogmbs = host->mb.ogmb;
     int *next_ogmb = &(host->next_ogmb);
 
 #ifdef WD7000_DEBUG
-    printk ("wd7000 mail_out: 0x%06lx", (long) scbptr);
+    printk ("wd7000_%s: 0x%08x", __FUNCTION__, (int) scbptr);
 #endif
 
     /* We first look for a free outgoing mailbox */
     save_flags (flags);
     cli ();
+
     ogmb = *next_ogmb;
     for (i = 0; i < OGMB_CNT; i++) {
 	if (ogmbs[ogmb].status == 0) {
@@ -954,10 +575,11 @@
 	else
 	    ogmb = (++ogmb) % OGMB_CNT;
     }
+
     restore_flags (flags);
 
 #ifdef WD7000_DEBUG
-    printk (", scb is 0x%06lx", (long) scbptr);
+    printk (", scb is 0x%08x", (int) scbptr);
 #endif
 
     if (i >= OGMB_CNT) {
@@ -978,7 +600,7 @@
     wd7000_enable_intr (host);
 
     start_ogmb = START_OGMB | ogmb;
-    command_out (host, &start_ogmb, 1);
+    wd7000_command_out (host, &start_ogmb, 1);
 
 #ifdef WD7000_DEBUG
     printk (", awaiting interrupt.\n");
@@ -988,7 +610,7 @@
 }
 
 
-int make_code (unsigned hosterr, unsigned scsierr)
+int make_code (uint hosterr, uint scsierr)
 {
 #ifdef WD7000_DEBUG
     int in_error = hosterr;
@@ -998,40 +620,51 @@
 	case 0:  /* Reserved */
                  hosterr = DID_ERROR;
                  break;
+
 	case 1:  /* Command Complete, no errors */
                  hosterr = DID_OK;
                  break;
+
 	case 2:  /* Command complete, error logged in scb status (scsierr) */
                  hosterr = DID_OK;
                  break;
+
 	case 4:  /* Command failed to complete - timeout */
                  hosterr = DID_TIME_OUT;
                  break;
+
 	case 5:  /* Command terminated; Bus reset by external device */
                  hosterr = DID_RESET;
                  break;
+
 	case 6:  /* Unexpected Command Received w/ host as target */
                  hosterr = DID_BAD_TARGET;
                  break;
+
 	case 80: /* Unexpected Reselection */
 	case 81: /* Unexpected Selection */
                  hosterr = DID_BAD_INTR;
                  break;
+
 	case 82: /* Abort Command Message  */
                  hosterr = DID_ABORT;
                  break;
+
 	case 83: /* SCSI Bus Software Reset */
 	case 84: /* SCSI Bus Hardware Reset */
                  hosterr = DID_RESET;
                  break;
+
 	default: /* Reserved */
                  hosterr = DID_ERROR;
     }
+
 #ifdef WD7000_DEBUG
     if (scsierr || hosterr)
 	printk ("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n",
 		scsierr, in_error, hosterr);
 #endif
+
     return (scsierr | (hosterr << 16));
 }
 
@@ -1039,14 +672,18 @@
 static void wd7000_scsi_done (Scsi_Cmnd *SCpnt)
 {
 #ifdef WD7000_DEBUG
-    printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt);
+    printk ("%s: 0x%08x\n", __FUNCTION__, (int) SCpnt);
 #endif
 
     SCpnt->SCp.phase = 0;
 }
 
 
-#define wd7000_intr_ack(host)   outb (0, host->iobase + ASC_INTR_ACK)
+static inline void wd7000_intr_ack (Adapter *host)
+{
+    outb (0, host->iobase + ASC_INTR_ACK);
+}
+
 
 void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -1061,16 +698,16 @@
     host->int_counter++;
 
 #ifdef WD7000_DEBUG
-    printk ("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host);
+    printk ("%s: irq = %d, host = 0x%08x\n", __FUNCTION__, irq, (int) host);
 #endif
 
     flag = inb (host->iobase + ASC_INTR_STAT);
 
 #ifdef WD7000_DEBUG
-    printk ("wd7000_intr_handle: intr stat = 0x%02x\n", flag);
+    printk ("%s: intr stat = 0x%02x\n", __FUNCTION__, flag);
 #endif
 
-    if (!(inb (host->iobase + ASC_STAT) & INT_IM)) {
+    if (! (inb (host->iobase + ASC_STAT) & INT_IM)) {
 	/* NB: these are _very_ possible if IRQ 15 is being used, since
 	 * it's the "garbage collector" on the 2nd 8259 PIC.  Specifically,
 	 * any interrupt signal into the 8259 which can't be identified
@@ -1080,7 +717,7 @@
 	 * problems would be indistinguishable from valid interrupts...
 	 */
 #ifdef WD7000_DEBUG
-	printk ("wd7000_intr_handle: phantom interrupt...\n");
+	printk ("%s: phantom interrupt...\n", __FUNCTION__);
 #endif
 	wd7000_intr_ack (host);
 	return;
@@ -1088,9 +725,9 @@
 
     if (flag & MB_INTR) {
 	/* The interrupt is for a mailbox */
-	if (!(flag & IMB_INTR)) {
+	if (! (flag & IMB_INTR)) {
 #ifdef WD7000_DEBUG
-	    printk ("wd7000_intr_handle: free outgoing mailbox\n");
+	    printk ("%s: free outgoing mailbox\n", __FUNCTION__);
 #endif
 	    /*
 	     * If sleep_on() and the "interrupt on free OGMB" command are
@@ -1104,15 +741,19 @@
 	    /* The interrupt is for an incoming mailbox */
 	    icmb = flag & MB_MASK;
 	    icmb_status = icmbs[icmb].status;
+
 	    if (icmb_status & 0x80) {	/* unsolicited - result in ICMB */
 #ifdef WD7000_DEBUG
-		printk ("wd7000_intr_handle: unsolicited interrupt 0x%02xh\n",
-			icmb_status);
+		printk ("%s: unsolicited interrupt 0x%02x\n",
+			__FUNCTION__, icmb_status);
 #endif
 		wd7000_intr_ack (host);
 		return;
 	    }
-	    scb = (struct scb *) scsi2int ((unchar *) icmbs[icmb].scbptr);
+
+	    /* Aaaargh! (Zaga) */
+	    scb = (Scb *) bus_to_virt (scsi2int ((unchar *) icmbs[icmb].scbptr));
+
 	    icmbs[icmb].status = 0;
 	    if (!(scb->op & ICB_OP_MASK)) {	/* an SCB is done */
 		SCpnt = scb->SCpnt;
@@ -1122,7 +763,7 @@
 		    errstatus = make_code (host_error, scsi_error);
 		    SCpnt->result = errstatus;
 
-		    free_scb (scb);
+		    scb_free (scb);
 
 		    SCpnt->scsi_done (SCpnt);
 		}
@@ -1136,6 +777,26 @@
     }
 
     wd7000_intr_ack (host);
+
+#ifdef WD7000_DEBUG
+    printk ("%s: return from interrupt handler\n", __FUNCTION__);
+#endif
+}
+
+
+void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
+{
+#if (LINUX_VERSION_CODE >= 0x020100)
+    ulong flags;
+
+    spin_lock_irqsave (&io_request_lock, flags);
+#endif
+
+    wd7000_intr_handle (irq, dev_id, regs);
+
+#if (LINUX_VERSION_CODE >= 0x020100)
+    spin_unlock_irqrestore (&io_request_lock, flags);
+#endif
 }
 
 
@@ -1143,33 +804,31 @@
 {
     register Scb *scb;
     register Sgb *sgb;
-    register unchar *cdb = (unchar *) SCpnt->cmnd;
-    register unchar idlun;
-    register short cdblen;
-    Adapter *host = (Adapter *) SCpnt->host->hostdata;
+    register Adapter *host = (Adapter *) SCpnt->host->hostdata;
+
+    if ((scb = scb_alloc ()) == NULL) {
+	printk ("%s: Cannot allocate SCB!\n", __FUNCTION__);
+	return (0);
+    }
 
-    cdblen = SCpnt->cmd_len;
-    idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
     SCpnt->scsi_done = done;
     SCpnt->SCp.phase = 1;
-    scb = alloc_scbs (1);
-    scb->idlun = idlun;
-    memcpy (scb->cdb, cdb, cdblen);
+    SCpnt->host_scribble = (unchar *) scb;
+    scb->idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
     scb->direc = 0x40;		/* Disable direction check */
-
     scb->SCpnt = SCpnt;		/* so we can find stuff later */
-    SCpnt->host_scribble = (unchar *) scb;
     scb->host = host;
+    memcpy (scb->cdb, SCpnt->cmnd, SCpnt->cmd_len);
 
     if (SCpnt->use_sg) {
 	struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
-	unsigned i;
+	uint i;
 
-	if (SCpnt->host->sg_tablesize == SG_NONE) {
-	    panic ("wd7000_queuecommand: scatter/gather not supported.\n");
-	}
+	if (SCpnt->host->sg_tablesize == SG_NONE)
+	    panic ("%s: scatter/gather not supported.\n", __FUNCTION__);
 #ifdef WD7000_DEBUG
-	printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg);
+	else
+	    printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg);
 #endif
 
 	sgb = scb->sgb;
@@ -1188,7 +847,7 @@
 	any2scsi (scb->maxlen, SCpnt->request_bufflen);
     }
 
-    while (!mail_out (host, scb));	/* keep trying */
+    while (! mail_out (host, scb));	/* keep trying */
 
     return (1);
 }
@@ -1196,7 +855,8 @@
 
 int wd7000_command (Scsi_Cmnd *SCpnt)
 {
-    wd7000_queuecommand (SCpnt, wd7000_scsi_done);
+    if (! wd7000_queuecommand (SCpnt, wd7000_scsi_done))
+	return (-1);
 
     while (SCpnt->SCp.phase > 0)
 	barrier ();		/* phase counts scbs down to 0 */
@@ -1207,32 +867,35 @@
 
 int wd7000_diagnostics (Adapter *host, int code)
 {
-    static IcbDiag icb =
-    {ICB_OP_DIAGNOSTICS};
+    static IcbDiag icb = { ICB_OP_DIAGNOSTICS };
     static unchar buf[256];
-    unsigned long timeout;
+    ulong timeout;
 
+    /*
+     * This routine is only called at init, so there should be OGMBs
+     * available.  I'm assuming so here.  If this is going to
+     * fail, I can just let the timeout catch the failure.
+     */
     icb.type = code;
     any2scsi (icb.len, sizeof (buf));
     any2scsi (icb.ptr, (int) &buf);
     icb.phase = 1;
+
+    mail_out (host, (Scb *) &icb);
+
     /*
-     * This routine is only called at init, so there should be OGMBs
-     * available.  I'm assuming so here.  If this is going to
-     * fail, I can just let the timeout catch the failure.
+     * Wait up to 2 seconds for completion.
      */
-    mail_out (host, (struct scb *) &icb);
-    timeout = jiffies + WAITnexttimeout;	/* wait up to 2 seconds */
-    while (icb.phase && jiffies < timeout)
-	barrier ();		/* wait for completion */
+    for (timeout = jiffies + WAITnexttimeout; icb.phase && (jiffies < timeout); )
+	barrier ();
 
     if (icb.phase) {
-	printk ("wd7000_diagnostics: timed out.\n");
+	printk ("%s: timed out.\n", __FUNCTION__);
 	return (0);
     }
+
     if (make_code (icb.vue | (icb.status << 8), 0)) {
-	printk ("wd7000_diagnostics: failed (0x%02x,0x%02x)\n",
-		icb.vue, icb.status);
+	printk ("%s: failed (0x%02x,0x%02x)\n", __FUNCTION__, icb.vue, icb.status);
 	return (0);
     }
 
@@ -1265,28 +928,35 @@
     host->control = 0;		/* this must always shadow ASC_CONTROL */
 
     if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
-	printk ("wd7000_init: WAIT timed out.\n");
+	printk ("%s: WAIT timed out.\n", __FUNCTION__);
 	return (0);		/* 0 = not ok */
     }
 
     if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) {
-	printk ("wd7000_init: ");
+	printk ("%s: ", __FUNCTION__);
 
 	switch (diag) {
 	    case 2:  printk ("RAM failure.\n");
 		     break;
+
 	    case 3:  printk ("FIFO R/W failed\n");
 		     break;
+
 	    case 4:  printk ("SBIC register R/W failed\n");
 		     break;
+
 	    case 5:  printk ("Initialization D-FF failed.\n");
 		     break;
+
 	    case 6:  printk ("Host IRQ D-FF failed.\n");
 		     break;
+
 	    case 7:  printk ("ROM checksum error.\n");
 		     break;
+
 	    default: printk ("diagnostic code 0x%02Xh received.\n", diag);
 	}
+
 	return (0);
     }
 
@@ -1294,30 +964,33 @@
     memset (&(host->mb), 0, sizeof (host->mb));
 
     /* Execute init command */
-    any2scsi ((unchar *) & (init_cmd.mailboxes), (int) &(host->mb));
-    if (!command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) {
-	printk ("wd7000_init: adapter initialization failed.\n");
+    any2scsi ((unchar *) &(init_cmd.mailboxes), (int) &(host->mb));
+
+    if (! wd7000_command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) {
+	printk ("%s: adapter initialization failed.\n", __FUNCTION__);
 	return (0);
     }
 
     if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) {
-	printk ("wd7000_init: WAIT timed out.\n");
+	printk ("%s: WAIT timed out.\n", __FUNCTION__);
 	return (0);
     }
 
-    if (request_irq (host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) {
-	printk ("wd7000_init: can't get IRQ %d.\n", host->irq);
+    if (request_irq (host->irq, do_wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) {
+	printk ("%s: can't get IRQ %d.\n", __FUNCTION__, host->irq);
 	return (0);
     }
+
     if (request_dma (host->dma, "wd7000")) {
-	printk ("wd7000_init: can't get DMA channel %d.\n", host->dma);
+	printk ("%s: can't get DMA channel %d.\n", __FUNCTION__, host->dma);
 	free_irq (host->irq, NULL);
 	return (0);
     }
+
     wd7000_enable_dma (host);
     wd7000_enable_intr (host);
 
-    if (!wd7000_diagnostics (host, ICB_DIAG_FULL)) {
+    if (! wd7000_diagnostics (host, ICB_DIAG_FULL)) {
 	free_dma (host->dma);
 	free_irq (host->irq, NULL);
 	return (0);
@@ -1329,30 +1002,31 @@
 
 void wd7000_revision (Adapter *host)
 {
-    static IcbRevLvl icb =
-    {ICB_OP_GET_REVISION};
+    static IcbRevLvl icb = { ICB_OP_GET_REVISION };
 
-    icb.phase = 1;
     /*
      * Like diagnostics, this is only done at init time, in fact, from
      * wd7000_detect, so there should be OGMBs available.  If it fails,
      * the only damage will be that the revision will show up as 0.0,
      * which in turn means that scatter/gather will be disabled.
      */
-    mail_out (host, (struct scb *) &icb);
+    icb.phase = 1;
+    mail_out (host, (Scb *) &icb);
+
     while (icb.phase)
 	barrier ();		/* wait for completion */
+
     host->rev1 = icb.primary;
     host->rev2 = icb.secondary;
 }
 
 
 #undef SPRINTF
-#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+#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;
+    ulong flags;
 
     save_flags (flags);
     cli ();
@@ -1375,9 +1049,9 @@
 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;
+    Scsi_Device *scd;
     Adapter *adapter;
-    unsigned long flags;
+    ulong flags;
     char *pos = buffer;
     short i;
 
@@ -1399,7 +1073,7 @@
     /*
      * Host not found!
      */
-    if (!host)
+    if (! host)
 	return (-ESRCH);
 
     /*
@@ -1455,9 +1129,15 @@
     /*
      * Display driver information for each device attached to the board.
      */
+#if (LINUX_VERSION_CODE >= 0x020100)
+    scd = host->host_queue;
+#else
+    scd = scsi_devices;
+#endif
+   
     SPRINTF ("\nAttached devices: %s\n", scd ? "" : "none");
 
-    for (; scd; scd = scd->next)
+    for ( ; scd; scd = scd->next)
 	if (scd->host->host_no == hostno) {
 	    SPRINTF ("  [Channel: %02d, Id: %02d, Lun: %02d]  ",
 		     scd->channel, scd->id, scd->lun);
@@ -1505,50 +1185,73 @@
 {
     short present = 0, biosaddr_ptr, sig_ptr, i, pass;
     short biosptr[NUM_CONFIGS];
-    unsigned iobase;
+    uint iobase;
     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;
+#ifdef WD7000_DEBUG
+    printk ("%s: started\n", __FUNCTION__);
+#endif
 
     /*
      * Set up SCB free list, which is shared by all adapters
      */
-    init_scbs ();
+    scbs_init ();
+
+    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;
 
     for (pass = 0; pass < NUM_CONFIGS; pass++) {
+	short bios_match = 1;
+
+#ifdef WD7000_DEBUG
+	printk ("%s: pass %d\n", __FUNCTION__, pass + 1);
+#endif
+
 	/*
 	 * 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 (biosaddr_ptr = 0; bios_match && (biosaddr_ptr < NUM_ADDRS); biosaddr_ptr++)
+	    for (sig_ptr = 0; bios_match && (sig_ptr < NUM_SIGNATURES); sig_ptr++) {
 		for (i = 0; i < pass; i++)
 		    if (biosptr[i] == biosaddr_ptr)
 			break;
 
-		if ((i == pass) &&
-		    !memcmp ((void *) (wd7000_biosaddr[biosaddr_ptr] +
-				       signatures[sig_ptr].ofs), signatures[sig_ptr].sig,
-			     signatures[sig_ptr].len))
-		    goto bios_matched;
-	    }
+		if (i == pass) {
+#if (LINUX_VERSION_CODE >= 0x020100)
+		    char *biosaddr = (char *) ioremap (wd7000_biosaddr[biosaddr_ptr] +
+							signatures[sig_ptr].ofs,
+							signatures[sig_ptr].len);
+#else
+		    char *biosaddr = (char *) (wd7000_biosaddr[biosaddr_ptr] +
+						signatures[sig_ptr].ofs);
+#endif
+		    bios_match = memcmp (biosaddr, signatures[sig_ptr].sig,
+						signatures[sig_ptr].len);
 
-      bios_matched:
-	/*
-	 * BIOS SIGNATURE has been found.
-	 */
+#if (LINUX_VERSION_CODE >= 0x020100)
+		    iounmap (biosaddr);
+#else
+#endif
+		    if (! bios_match) {
+			/*
+			 * BIOS SIGNATURE has been found.
+			 */
+			biosptr[pass] = biosaddr_ptr;
 #ifdef WD7000_DEBUG
-	printk ("wd7000_detect: pass %d\n", pass + 1);
+			printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n",
+				wd7000_biosaddr[biosaddr_ptr]);
+#endif
+		    }
+		}
+	    }
 
-	if (biosaddr_ptr == NUM_ADDRS)
+#ifdef WD7000_DEBUG
+	if (bios_match)
 	    printk ("WD-7000 SST BIOS not detected...\n");
-	else
-	    printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n",
-		    wd7000_biosaddr[biosaddr_ptr]);
 #endif
 
 	if (configs[pass].irq < 0)
@@ -1557,13 +1260,12 @@
 	iobase = configs[pass].iobase;
 
 #ifdef WD7000_DEBUG
-	printk ("wd7000_detect: check IO 0x%x region...\n", iobase);
+	printk ("%s: check IO 0x%x region...\n", __FUNCTION__, iobase);
 #endif
 
-	if (!check_region (iobase, 4)) {
-
+	if (! check_region (iobase, 4)) {
 #ifdef WD7000_DEBUG
-	    printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
+	    printk ("%s: ASC reset (IO 0x%x) ...", __FUNCTION__, iobase);
 #endif
 	    /*
 	     * ASC reset...
@@ -1596,7 +1298,7 @@
 		host = (Adapter *) sh->hostdata;
 
 #ifdef WD7000_DEBUG
-		printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host);
+		printk ("%s: adapter allocated at 0x%x\n", __FUNCTION__, (int) host);
 #endif
 
 		memset (host, 0, sizeof (Adapter));
@@ -1610,14 +1312,12 @@
 		host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
 
 #ifdef WD7000_DEBUG
-		printk ("wd7000_detect: Trying init WD-7000 card at IO "
-			"0x%x, IRQ %d, DMA %d...\n",
-			host->iobase, host->irq, host->dma);
+		printk ("%s: Trying to init WD-7000 card at IO 0x%x, IRQ %d, DMA %d...\n",
+			__FUNCTION__, host->iobase, host->irq, host->dma);
 #endif
 
-		if (!wd7000_init (host)) {	/* Initialization failed */
+		if (! wd7000_init (host)) {	/* Initialization failed */
 		    scsi_unregister (sh);
-
 		    continue;
 		}
 
@@ -1639,9 +1339,6 @@
 
 		present++;	/* count it */
 
-		if (biosaddr_ptr != NUM_ADDRS)
-		    biosptr[pass] = biosaddr_ptr;
-
 		printk ("Western Digital WD-7000 (rev %d.%d) ",
 			host->rev1, host->rev2);
 		printk ("using IO 0x%x, IRQ %d, DMA %d.\n",
@@ -1653,12 +1350,12 @@
 
 #ifdef WD7000_DEBUG
 	else
-	    printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase);
+	    printk ("%s: IO 0x%x region is already allocated!\n", __FUNCTION__, iobase);
 #endif
 
     }
 
-    if (!present)
+    if (! present)
 	printk ("Failed initialization of WD-7000 SCSI card!\n");
 
     return (present);
@@ -1673,7 +1370,7 @@
     Adapter *host = (Adapter *) SCpnt->host->hostdata;
 
     if (inb (host->iobase + ASC_STAT) & INT_IM) {
-	printk ("wd7000_abort: lost interrupt\n");
+	printk ("%s: lost interrupt\n", __FUNCTION__);
 	wd7000_intr_handle (host->irq, NULL, NULL);
 
 	return (SCSI_ABORT_SUCCESS);
@@ -1686,7 +1383,7 @@
 /*
  *  I also have no idea how to do a reset...
  */
-int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int flags)
+int wd7000_reset (Scsi_Cmnd *SCpnt, uint flags)
 {
     return (SCSI_RESET_PUNT);
 }
@@ -1698,7 +1395,7 @@
 int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
 {
 #ifdef WD7000_DEBUG
-    printk ("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity);
+    printk ("%s: dev=%s, size=%d, ", __FUNCTION__, kdevname (dev), disk->capacity);
 #endif
 
     /*
@@ -1720,8 +1417,9 @@
 	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");
+	    printk ("%s: unable to verify geometry for disk with >1GB.\n"
+		    "                  using extended translation.\n",
+		    __FUNCTION__);
 
 	    ip[0] = 255;
 	    ip[1] = 63;
@@ -1733,7 +1431,8 @@
 	    ip[2] = info[2];
 
 	    if (info[0] == 255)
-		printk ("wd7000_biosparam: current partition table is using extended translation.\n");
+		printk ("%s: current partition table is using extended translation.\n",
+			__FUNCTION__);
 	}
     }
 
diff -u --recursive --new-file v2.0.35/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h
--- v2.0.35/linux/drivers/scsi/wd7000.h	Sun Nov 15 10:49:45 1998
+++ linux/drivers/scsi/wd7000.h	Sun Nov 15 10:33:10 1998
@@ -1,5 +1,3 @@
-#ifndef _WD7000_H
-
 /* $Id: $
  *
  * Header file for the WD-7000 driver for Linux
@@ -10,19 +8,12 @@
  *
  * Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr>  Jun 1997.
  */
+#ifndef _WD7000_H
 
+#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 
-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 *, unsigned int);
-int wd7000_biosparam (Disk *, kdev_t, int *);
-
 #ifndef NULL
 #define NULL 0L
 #endif
@@ -41,18 +32,415 @@
 #define WD7000_Q    16
 #define WD7000_SG   16
 
-#define WD7000 { NULL, NULL,            \
-	NULL,		                \
-	NULL,		                \
-	"Western Digital WD-7000",      \
-	wd7000_detect,                  \
-	NULL,				\
-	NULL,				\
-	wd7000_command,			\
-	wd7000_queuecommand,		\
-	wd7000_abort,			\
-	wd7000_reset,			\
-	NULL,                           \
-	wd7000_biosparam,               \
-	WD7000_Q, 7, WD7000_SG, 1, 0, 1, ENABLE_CLUSTERING}
+#ifdef WD7000_DEFINES
+/*
+ *  Mailbox structure sizes.
+ *  I prefer to keep the number of ICMBs much larger than the number of
+ *  OGMBs.  OGMBs are used very quickly by the driver to start one or
+ *  more commands, while ICMBs are used by the host adapter per command.
+ */
+#define OGMB_CNT	16
+#define ICMB_CNT	32
+
+/*
+ *  Scb's are shared by all active adapters.  If you'd rather conserve
+ *  memory, use a smaller number (> 0, of course) - things will should
+ *  still work OK.
+ */
+#define MAX_SCBS	(4 * WD7000_Q)
+
+/*
+ *  WD7000-specific mailbox structure
+ */
+typedef volatile struct {
+    unchar status;
+    unchar scbptr[3];		/* SCSI-style - MSB first (big endian) */
+} Mailbox;
+
+/*
+ *  This structure should contain all per-adapter global data.  I.e., any
+ *  new global per-adapter data should put in here.
+ */
+typedef struct {
+    struct Scsi_Host *sh;	/* Pointer to Scsi_Host structure    */
+    int iobase;			/* This adapter's I/O base address   */
+    int irq;			/* This adapter's IRQ level          */
+    int dma;			/* This adapter's DMA channel        */
+    int int_counter;		/* This adapter's interrupt counter  */
+    int bus_on;			/* This adapter's BUS_ON time        */
+    int bus_off;		/* This adapter's BUS_OFF time       */
+    struct {			/* This adapter's mailboxes          */
+	Mailbox ogmb[OGMB_CNT];	/* Outgoing mailboxes                */
+	Mailbox icmb[ICMB_CNT];	/* Incoming mailboxes                */
+    } mb;
+    int next_ogmb;		/* to reduce contention at mailboxes */
+    unchar control;		/* shadows CONTROL port value        */
+    unchar rev1;		/* filled in by wd7000_revision      */
+    unchar rev2;
+} Adapter;
+
+
+/*
+ * possible irq range
+ */
+#define IRQ_MIN		3
+#define IRQ_MAX		15
+#define IRQS		(IRQ_MAX - IRQ_MIN + 1)
+
+#define BUS_ON		64	/* x 125ns = 8000ns (BIOS default) */
+#define BUS_OFF		15	/* x 125ns = 1875ns (BIOS default) */
+
+/*
+ *  Standard Adapter Configurations - used by wd7000_detect
+ */
+typedef struct {
+    short irq;		/* IRQ level                                  */
+    short dma;		/* DMA channel                                */
+    uint iobase;	/* I/O base address                           */
+    short bus_on;	/* Time that WD7000 spends on the AT-bus when */
+			/* transferring data. BIOS default is 8000ns. */
+    short bus_off;	/* Time that WD7000 spends OFF THE BUS after  */
+			/* while it is transferring data.             */
+			/* BIOS default is 1875ns                     */
+} Config;
+
+
+/*
+ *  The following list defines strings to look for in the BIOS that identify
+ *  it as the WD7000-FASST2 SST BIOS.  I suspect that something should be
+ *  added for the Future Domain version.
+ */
+typedef struct {
+    const char *sig;		/* String to look for            */
+    ulong ofs;			/* offset from BIOS base address */
+    uint len;			/* length of string              */
+} Signature;
+
+/*
+ *  I/O Port Offsets and Bit Definitions
+ *  4 addresses are used.  Those not defined here are reserved.
+ */
+#define ASC_STAT	0	/* Status,  Read          */
+#define ASC_COMMAND	0	/* Command, Write         */
+#define ASC_INTR_STAT	1	/* Interrupt Status, Read */
+#define ASC_INTR_ACK	1	/* Acknowledge, Write     */
+#define ASC_CONTROL	2	/* Control, Write         */
+
+/*
+ * ASC Status Port
+ */
+#define INT_IM		0x80	/* Interrupt Image Flag           */
+#define CMD_RDY		0x40	/* Command Port Ready             */
+#define CMD_REJ		0x20	/* Command Port Byte Rejected     */
+#define ASC_INIT        0x10	/* ASC Initialized Flag           */
+#define ASC_STATMASK    0xf0	/* The lower 4 Bytes are reserved */
+
+/*
+ * COMMAND opcodes
+ *
+ *  Unfortunately, I have no idea how to properly use some of these commands,
+ *  as the OEM manual does not make it clear.  I have not been able to use
+ *  enable/disable unsolicited interrupts or the reset commands with any
+ *  discernible effect whatsoever.  I think they may be related to certain
+ *  ICB commands, but again, the OEM manual doesn't make that clear.
+ */
+#define NO_OP			0	/* NO-OP toggles CMD_RDY bit in ASC_STAT  */
+#define INITIALIZATION		1	/* initialization (10 bytes)              */
+#define DISABLE_UNS_INTR	2	/* disable unsolicited interrupts         */
+#define ENABLE_UNS_INTR		3	/* enable unsolicited interrupts          */
+#define INTR_ON_FREE_OGMB	4	/* interrupt on free OGMB                 */
+#define SOFT_RESET		5	/* SCSI bus soft reset                    */
+#define HARD_RESET_ACK		6	/* SCSI bus hard reset acknowledge        */
+#define START_OGMB		0x80	/* start command in OGMB (n)              */
+#define SCAN_OGMBS		0xc0	/* start multiple commands, signature (n) */
+					/*    where (n) = lower 6 bits            */
+/*
+ * For INITIALIZATION:
+ */
+typedef struct {
+    unchar op;			/* command opcode (= 1)                    */
+    unchar ID;			/* Adapter's SCSI ID                       */
+    unchar bus_on;		/* Bus on time, x 125ns (see below)        */
+    unchar bus_off;		/* Bus off time, ""         ""             */
+    unchar rsvd;		/* Reserved                                */
+    unchar mailboxes[3];	/* Address of Mailboxes, MSB first         */
+    unchar ogmbs;		/* Number of outgoing MBs, max 64, 0,1 = 1 */
+    unchar icmbs;		/* Number of incoming MBs,   ""       ""   */
+} InitCmd;
+
+/*
+ * Interrupt Status Port - also returns diagnostic codes at ASC reset
+ *
+ * if msb is zero, the lower bits are diagnostic status
+ * Diagnostics:
+ * 01   No diagnostic error occurred
+ * 02   RAM failure
+ * 03   FIFO R/W failed
+ * 04   SBIC register read/write failed
+ * 05   Initialization D-FF failed
+ * 06   Host IRQ D-FF failed
+ * 07   ROM checksum error
+ * Interrupt status (bitwise):
+ * 10NNNNNN   outgoing mailbox NNNNNN is free
+ * 11NNNNNN   incoming mailbox NNNNNN needs service
+ */
+#define MB_INTR		0xC0	/* Mailbox Service possible/required */
+#define IMB_INTR	0x40	/* 1 Incoming / 0 Outgoing           */
+#define MB_MASK		0x3f	/* mask for mailbox number           */
+
+/*
+ * CONTROL port bits
+ */
+#define INT_EN		0x08	/* Interrupt Enable */
+#define DMA_EN		0x04	/* DMA Enable       */
+#define SCSI_RES	0x02	/* SCSI Reset       */
+#define ASC_RES		0x01	/* ASC Reset        */
+
+/*
+ * Driver data structures:
+ *   - mb and scbs are required for interfacing with the host adapter.
+ *     An SCB has extra fields not visible to the adapter; mb's
+ *     _cannot_ do this, since the adapter assumes they are contiguous in
+ *     memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
+ *     to access them.
+ *   - An icb is for host-only (non-SCSI) commands.  ICBs are 16 bytes each;
+ *     the additional bytes are used only by the driver.
+ *   - For now, a pool of SCBs are kept in global storage by this driver,
+ *     and are allocated and freed as needed.
+ *
+ *  The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
+ *  not when it has finished.  Since the SCB must be around for completion,
+ *  problems arise when SCBs correspond to OGMBs, which may be reallocated
+ *  earlier (or delayed unnecessarily until a command completes).
+ *  Mailboxes are used as transient data structures, simply for
+ *  carrying SCB addresses to/from the 7000-FASST2.
+ *
+ *  Note also since SCBs are not "permanently" associated with mailboxes,
+ *  there is no need to keep a global list of Scsi_Cmnd pointers indexed
+ *  by OGMB.   Again, SCBs reference their Scsi_Cmnds directly, so mailbox
+ *  indices need not be involved.
+ */
+
+/*
+ *  WD7000-specific scatter/gather element structure
+ */
+typedef struct {
+    unchar len[3];
+    unchar ptr[3];		/* Also SCSI-style - MSB first */
+} Sgb;
+
+typedef struct {		/* Command Control Block 5.4.1               */
+    unchar op;			/* Command Control Block Operation Code      */
+    unchar idlun;		/* op=0,2:Target Id, op=1:Initiator Id       */
+				/* Outbound data transfer, length is checked */
+				/* Inbound data transfer, length is checked  */
+				/* Logical Unit Number                       */
+    unchar cdb[12];		/* SCSI Command Block                        */
+    volatile unchar status;	/* SCSI Return Status                        */
+    volatile unchar vue;	/* Vendor Unique Error Code                  */
+    unchar maxlen[3];		/* Maximum Data Transfer Length              */
+    unchar dataptr[3];		/* SCSI Data Block Pointer                   */
+    unchar linkptr[3];		/* Next Command Link Pointer                 */
+    unchar direc;		/* Transfer Direction                        */
+    unchar reserved2[6];	/* SCSI Command Descriptor Block             */
+				/* end of hardware SCB                       */
+    Scsi_Cmnd *SCpnt;		/* Scsi_Cmnd using this SCB                  */
+    Sgb sgb[WD7000_SG];		/* Scatter/gather list for this SCB          */
+    Adapter *host;		/* host adapter                              */
+    unchar used;		/* flag                                      */
+} Scb;
+
+/*
+ *  This driver is written to allow host-only commands to be executed.
+ *  These use a 16-byte block called an ICB.  The format is extended by the
+ *  driver to 18 bytes, to support the status returned in the ICMB and
+ *  an execution phase code.
+ *
+ *  There are other formats besides these; these are the ones I've tried
+ *  to use.  Formats for some of the defined ICB opcodes are not defined
+ *  (notably, get/set unsolicited interrupt status) in my copy of the OEM
+ *  manual, and others are ambiguous/hard to follow.
+ */
+#define ICB_OP_MASK		0x80	/* distinguishes scbs from icbs        */
+#define ICB_OP_OPEN_RBUF	0x80	/* open receive buffer                 */
+#define ICB_OP_RECV_CMD		0x81	/* receive command from initiator      */
+#define ICB_OP_RECV_DATA	0x82	/* receive data from initiator         */
+#define ICB_OP_RECV_SDATA	0x83	/* receive data with status from init. */
+#define ICB_OP_SEND_DATA	0x84	/* send data with status to initiator  */
+#define ICB_OP_SEND_STAT	0x86	/* send command status to initiator    */
+					/* 0x87 is reserved                    */
+#define ICB_OP_READ_INIT	0x88	/* read initialization bytes           */
+#define ICB_OP_READ_ID		0x89	/* read adapter's SCSI ID              */
+#define ICB_OP_SET_UMASK	0x8A	/* set unsolicited interrupt mask      */
+#define ICB_OP_GET_UMASK	0x8B	/* read unsolicited interrupt mask     */
+#define ICB_OP_GET_REVISION	0x8C	/* read firmware revision level        */
+#define ICB_OP_DIAGNOSTICS	0x8D	/* execute diagnostics                 */
+#define ICB_OP_SET_EPARMS	0x8E	/* set execution parameters            */
+#define ICB_OP_GET_EPARMS	0x8F	/* read execution parameters           */
+
+typedef struct {
+    unchar op;
+    unchar IDlun;		/* Initiator SCSI ID/lun     */
+    unchar len[3];		/* command buffer length     */
+    unchar ptr[3];		/* command buffer address    */
+    unchar rsvd[7];		/* reserved                  */
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
+} IcbRecvCmd;
+
+typedef struct {
+    unchar op;
+    unchar IDlun;		/* Target SCSI ID/lun                  */
+    unchar stat;		/* (outgoing) completion status byte 1 */
+    unchar rsvd[12];		/* reserved                            */
+    volatile unchar vue;	/* vendor-unique error code            */
+    volatile unchar status;	/* returned (icmb) status              */
+    volatile unchar phase;	/* used by interrupt handler           */
+} IcbSendStat;
+
+typedef struct {
+    unchar op;
+    volatile unchar primary;	/* primary revision level (returned)   */
+    volatile unchar secondary;	/* secondary revision level (returned) */
+    unchar rsvd[12];		/* reserved                            */
+    volatile unchar vue;	/* vendor-unique error code            */
+    volatile unchar status;	/* returned (icmb) status              */
+    volatile unchar phase;	/* used by interrupt handler           */
+} IcbRevLvl;
+
+typedef struct {	/* I'm totally guessing here */
+    unchar op;
+    volatile unchar mask[14];	/* mask bits                 */
+#if 0
+    unchar rsvd[12];		/* reserved                  */
 #endif
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
+} IcbUnsMask;
+
+typedef struct {
+    unchar op;
+    unchar type;		/* diagnostics type code (0-3) */
+    unchar len[3];		/* buffer length               */
+    unchar ptr[3];		/* buffer address              */
+    unchar rsvd[7];		/* reserved                    */
+    volatile unchar vue;	/* vendor-unique error code    */
+    volatile unchar status;	/* returned (icmb) status      */
+    volatile unchar phase;	/* used by interrupt handler   */
+} IcbDiag;
+
+#define ICB_DIAG_POWERUP	0	/* Power-up diags only       */
+#define ICB_DIAG_WALKING	1	/* walking 1's pattern       */
+#define ICB_DIAG_DMA		2	/* DMA - system memory diags */
+#define ICB_DIAG_FULL		3	/* do both 1 & 2             */
+
+typedef struct {
+    unchar op;
+    unchar rsvd1;		/* reserved                  */
+    unchar len[3];		/* parms buffer length       */
+    unchar ptr[3];		/* parms buffer address      */
+    unchar idx[2];		/* index (MSB-LSB)           */
+    unchar rsvd2[5];		/* reserved                  */
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
+} IcbParms;
+
+typedef struct {
+    unchar op;
+    unchar data[14];		/* format-specific data      */
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
+} IcbAny;
+
+typedef union {
+    unchar op;			/* ICB opcode                     */
+    IcbRecvCmd recv_cmd;	/* format for receive command     */
+    IcbSendStat send_stat;	/* format for send status         */
+    IcbRevLvl rev_lvl;		/* format for get revision level  */
+    IcbDiag diag;		/* format for execute diagnostics */
+    IcbParms eparms;		/* format for get/set exec parms  */
+    IcbAny icb;			/* generic format                 */
+    unchar data[18];
+} Icb;
+
+#define WAITnexttimeout		200	/* 2 seconds */
+
+typedef union {			/* let's cheat... */
+    int i;
+    unchar u[sizeof (int)];	/* the sizeof(int) makes it more portable */
+} i_u;
+
+#endif /* WD7000_DEFINES */
+
+
+#if (LINUX_VERSION_CODE >= 0x020100)
+
+#define WD7000 {					\
+    proc_dir:		&proc_scsi_wd7000,		\
+    proc_info:		wd7000_proc_info,		\
+    name:		"Western Digital WD-7000",	\
+    detect:		wd7000_detect,			\
+    command:		wd7000_command,			\
+    queuecommand:	wd7000_queuecommand,		\
+    abort:		wd7000_abort,			\
+    reset:		wd7000_reset,			\
+    bios_param:		wd7000_biosparam,		\
+    can_queue:		WD7000_Q,			\
+    this_id:		7,				\
+    sg_tablesize:	WD7000_SG,			\
+    cmd_per_lun:	1,				\
+    unchecked_isa_dma:	1,				\
+    use_clustering:	ENABLE_CLUSTERING,		\
+    use_new_eh_code:	0				\
+}
+
+#else /* Use old scsi code */
+
+#define WD7000 {					\
+    proc_dir:		&proc_scsi_wd7000,		\
+    proc_info:		wd7000_proc_info,		\
+    name:		"Western Digital WD-7000",	\
+    detect:		wd7000_detect,			\
+    command:		wd7000_command,			\
+    queuecommand:	wd7000_queuecommand,		\
+    abort:		wd7000_abort,			\
+    reset:		wd7000_reset,			\
+    bios_param:		wd7000_biosparam,		\
+    can_queue:		WD7000_Q,			\
+    this_id:		7,				\
+    sg_tablesize:	WD7000_SG,			\
+    cmd_per_lun:	1,				\
+    unchecked_isa_dma:	1,				\
+    use_clustering:	ENABLE_CLUSTERING,		\
+}
+
+#endif /* LINUX_VERSION_CODE */
+
+
+extern struct proc_dir_entry proc_scsi_wd7000;
+
+
+#ifdef WD7000_DEFINES
+int  wd7000_diagnostics    (Adapter *, int);
+int  wd7000_init           (Adapter *);
+void wd7000_revision       (Adapter *);
+#endif /* WD7000_DEFINES */
+
+void wd7000_setup          (char *, int *);
+int  make_code             (uint, uint);
+void wd7000_intr_handle    (int, void *, struct pt_regs *);
+void do_wd7000_intr_handle (int, void *, struct pt_regs *);
+int  wd7000_queuecommand   (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int  wd7000_command        (Scsi_Cmnd *);
+int  wd7000_set_info       (char *, int, struct Scsi_Host *);
+int  wd7000_proc_info      (char *, char **, off_t, int, int, int);
+int  wd7000_detect         (Scsi_Host_Template *);
+int  wd7000_abort          (Scsi_Cmnd *);
+int  wd7000_reset          (Scsi_Cmnd *, uint);
+int  wd7000_biosparam      (Disk *, kdev_t, int *);
+
+#endif /* _WD7000_H */
diff -u --recursive --new-file v2.0.35/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.0.35/linux/drivers/sound/Makefile	Sun Jun 30 01:43:41 1996
+++ linux/drivers/sound/Makefile	Sun Nov 15 10:33:11 1998
@@ -146,11 +146,10 @@
 	$(LD) -r -o sound.o $(FIXEDOBJS) sound.a
 
 modules: local.h sound.o
-	ln -fs `pwd`/sound.o $(TOPDIR)/modules/sound.o
-
+	ln -fs ../drivers/sound/sound.o $(TOPDIR)/modules/sound.o
 
 lowlevel/lowlevel.o: dummy
-		cd lowlevel;make
+	cd lowlevel; make
 
 ifdef USE_DEPEND
 #
diff -u --recursive --new-file v2.0.35/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v2.0.35/linux/drivers/sound/Readme	Sun Aug 31 08:36:50 1997
+++ linux/drivers/sound/Readme	Sun Nov 15 10:33:11 1998
@@ -157,7 +157,7 @@
 	with impossible parameters. Check that the application is
 	for sound driver version 2.X or later.
 
-In general the printout of of /dev/sndstat should tell what is the problem.
+In general the printout of /dev/sndstat should tell what is the problem.
 It's possible that there are bugs in the sound driver but 99% of the problems
 reported to me are caused by somehow incorrect setup during "make config".
 
diff -u --recursive --new-file v2.0.35/linux/drivers/sound/ad1848_mixer.h linux/drivers/sound/ad1848_mixer.h
--- v2.0.35/linux/drivers/sound/ad1848_mixer.h	Sun Jun 30 01:43:36 1996
+++ linux/drivers/sound/ad1848_mixer.h	Sun Nov 15 10:33:11 1998
@@ -17,7 +17,7 @@
  * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
  * Soundcard manufacturers have connected actual inputs (CD, synth, line,
  * etc) to these inputs in different order. Therefore it's difficult
- * to assign mixer channels to to these inputs correctly. The following
+ * to assign mixer channels to these inputs correctly. The following
  * contains two alternative mappings. The first one is for GUS MAX and
  * the second is just a generic one (line1, line2 and line3).
  * (Actually this is not a mapping but rather some kind of interleaving
diff -u --recursive --new-file v2.0.35/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.0.35/linux/drivers/sound/sb_common.c	Mon Jul 13 13:46:37 1998
+++ linux/drivers/sound/sb_common.c	Sun Nov 15 10:33:11 1998
@@ -1196,11 +1196,13 @@
     case MDL_SB16:
       if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
 	{
-	  printk ("SB16: Invalid MIDI port %x\n", hw_config->irq);
+	  printk ("SB16: Invalid MIDI port %x\n", hw_config->io_base);
 	  return 0;
 	}
       hw_config->name = "Sound Blaster 16";
       hw_config->irq = -devc->irq;
+      hw_config->dma = -1;
+      hw_config->dma2 = -1;
       sb16_set_mpu_port(devc, hw_config);
       break;
 
diff -u --recursive --new-file v2.0.35/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.0.35/linux/drivers/sound/sequencer.c	Mon Aug 25 13:25:04 1997
+++ linux/drivers/sound/sequencer.c	Sun Nov 15 10:33:11 1998
@@ -1149,7 +1149,10 @@
       }
 
   if (!max_synthdev && !max_mididev)
-    return -(ENXIO);
+    {
+      sequencer_busy = 0;
+      return -(ENXIO);
+    }
 
   synth_open_mask = 0;
 
diff -u --recursive --new-file v2.0.35/linux/fs/block_dev.c linux/fs/block_dev.c
--- v2.0.35/linux/fs/block_dev.c	Tue Mar 11 14:35:16 1997
+++ linux/fs/block_dev.c	Sun Nov 15 10:33:11 1998
@@ -203,8 +203,11 @@
 			blocks = rblocks;
 		
 	}
-	if (block + blocks > size)
+	if (block + blocks > size) {
 		blocks = size - block;
+		if (blocks == 0)
+			return 0;
+	}
 
 	/* We do this in a two stage process.  We first try to request
 	   as many blocks as we can, then we wait for the first one to
diff -u --recursive --new-file v2.0.35/linux/fs/buffer.c linux/fs/buffer.c
--- v2.0.35/linux/fs/buffer.c	Sun Nov 15 10:49:47 1998
+++ linux/fs/buffer.c	Sun Nov 15 10:33:11 1998
@@ -1005,11 +1005,22 @@
 		 * This is no longer true, it is GFP_BUFFER again, the
 		 * swapping code now knows not to perform I/O when that
 		 * GFP level is specified... -DaveM
+		 * 
+		 * Ouch, another bug!  get_free_page() does not call
+		 * try_to_free_page() if priority == GFP_BUFFER.  This
+		 * lets kswapd get into a lockup situation if there is
+		 * no free space for buffer growth but we need more
+		 * memory for a buffer_head for swapping.  If memory is
+		 * full of recyclable buffers, we deadlock because
+		 * kswapd won't recycle them!  Use GFP_IO instead: it
+		 * still won't recurse (GFP_IO sets can_do_io to zero in
+		 * try_to_free_page), but it lets us recover those
+		 * buffer heads.  --sct
 		 */
 		/* we now use kmalloc() here instead of gfp as we want
                    to be able to easily release buffer heads - they
                    took up quite a bit of memory (tridge) */
-		bh = (struct buffer_head *) kmalloc(sizeof(*bh),GFP_BUFFER);
+		bh = (struct buffer_head *) kmalloc(sizeof(*bh),GFP_IO);
 		if (bh) {
 			put_unused_buffer_head(bh);
 			nr_buffer_heads++;
diff -u --recursive --new-file v2.0.35/linux/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- v2.0.35/linux/fs/ext2/ialloc.c	Sun Nov 15 10:49:47 1998
+++ linux/fs/ext2/ialloc.c	Sun Nov 15 10:33:12 1998
@@ -205,14 +205,30 @@
 			inode->i_nlink);
 		return;
 	}
-	if (!inode->i_sb) {
+	sb = inode->i_sb;
+	if (!sb) {
 		printk("ext2_free_inode: inode on nonexistent device\n");
 		return;
 	}
 
 	ext2_debug ("freeing inode %lu\n", inode->i_ino);
 
-	sb = inode->i_sb;
+	/* We need to kill quota references now, before grabbing the
+	 * superblock lock because writing the quota out to disk
+	 * may need to lock the superblock as well.
+	 *
+	 * It is safe to do this early instead of the original
+	 * places because we cannot be here in ext2_free_inode
+	 * if any other references to this inode exist at all.
+	 *
+	 * Based upon a 2.1.x fix by Bill Hawes.   --DaveM
+	 */
+	if (sb->dq_op) {
+		sb->dq_op->free_inode (inode, 1);
+		if (IS_WRITABLE (inode))
+			sb->dq_op->drop(inode);
+	}
+
 	lock_super (sb);
 	if (inode->i_ino < EXT2_FIRST_INO(sb) ||
 	    inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) {
@@ -249,8 +265,6 @@
 		ll_rw_block (WRITE, 1, &bh);
 		wait_on_buffer (bh);
 	}
-	if (sb->dq_op)
-		sb->dq_op->free_inode (inode, 1);
 	sb->s_dirt = 1;
 	clear_inode (inode);
 	unlock_super (sb);
diff -u --recursive --new-file v2.0.35/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c
--- v2.0.35/linux/fs/fat/fatfs_syms.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/fat/fatfs_syms.c	Sun Nov 15 10:33:12 1998
@@ -6,7 +6,6 @@
  */
 #define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
 #include <linux/version.h>
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/mm.h>
diff -u --recursive --new-file v2.0.35/linux/fs/fat/file.c linux/fs/fat/file.c
--- v2.0.35/linux/fs/fat/file.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/fat/file.c	Sun Nov 15 10:33:13 1998
@@ -324,6 +324,12 @@
 #else
 	if (count <= 0) return 0;
 #endif
+	if (filp->f_pos + count > 0x7FFFFFFFL) {
+		count = 0x7FFFFFFFL - filp->f_pos;
+		if (!count)
+			return -EFBIG;
+	}
+
 	error = carry = 0;
 	for (start = buf; count || carry; count -= size) {
 		while (!(sector = fat_smap(inode,filp->f_pos >> SECTOR_BITS)))
diff -u --recursive --new-file v2.0.35/linux/fs/fat/misc.c linux/fs/fat/misc.c
--- v2.0.35/linux/fs/fat/misc.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/fat/misc.c	Sun Nov 15 10:33:13 1998
@@ -515,7 +515,7 @@
 /*
  * raw_scan performs raw_scan_sector on any sector.
  *
- * NOTE: raw_scan must not be used on a directory that is is the process of
+ * NOTE: raw_scan must not be used on a directory that is the process of
  *       being created.
  */
 
diff -u --recursive --new-file v2.0.35/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.0.35/linux/fs/isofs/inode.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/isofs/inode.c	Sun Nov 15 10:33:13 1998
@@ -587,7 +587,7 @@
 	int i;
 
 	if (block<0) {
-		printk("_isofs_bmap: block<0");
+		printk("_isofs_bmap: block<0\n");
 		return 0;
 	}
 
@@ -597,7 +597,8 @@
 	 * If we are beyond the end of this file, don't give out any
 	 * blocks.
 	 */
-	if( b_off > inode->i_size )
+	if( (b_off > inode->i_size) || 
+		((b_off ==  inode->i_size) && (b_off & (PAGE_SIZE - 1))) )
 	  {
 	    off_t	max_legal_read_offset;
 
@@ -614,7 +615,7 @@
 	    if( b_off >= max_legal_read_offset )
 	      {
 
-		printk("_isofs_bmap: block>= EOF(%d, %ld)", block, 
+		printk("_isofs_bmap: block>= EOF(%d, %ld)\n", block, 
 		       inode->i_size);
 	      }
 	    return 0;
diff -u --recursive --new-file v2.0.35/linux/fs/isofs/joliet.c linux/fs/isofs/joliet.c
--- v2.0.35/linux/fs/isofs/joliet.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/isofs/joliet.c	Sun Nov 15 10:33:13 1998
@@ -7,7 +7,6 @@
  */
 
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/nls.h>
 #include <linux/malloc.h>
 #include <linux/iso_fs.h>
@@ -95,6 +94,13 @@
 	}
 	if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) {
 		len -= 2;
+	}
+
+	/*
+	 * Windows doesn't like periods at the end of a name
+	 */
+	while (len >= 2 && (outname[len-1] == '.')) {
+		len--;
 	}
 
         if (inode->i_sb->u.isofs_sb.s_name_check == 'r') {
diff -u --recursive --new-file v2.0.35/linux/fs/locks.c linux/fs/locks.c
--- v2.0.35/linux/fs/locks.c	Mon Jul 13 13:46:37 1998
+++ linux/fs/locks.c	Sun Nov 15 10:33:13 1998
@@ -177,7 +177,7 @@
 /* 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.
+ * it seems like the reasonable thing to do.
  */
 static void locks_insert_block(struct file_lock *blocker, 
 			       struct file_lock *waiter)
diff -u --recursive --new-file v2.0.35/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.0.35/linux/fs/msdos/namei.c	Mon Jul 13 13:46:38 1998
+++ linux/fs/msdos/namei.c	Sun Nov 15 10:33:13 1998
@@ -369,7 +369,7 @@
 	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 		pos = 0;
 		bh = NULL;
-		while (fat_get_entry(dir,&pos,&bh,&de) > -1)
+		while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
 			/* Ignore vfat longname entries */
 			if (de->attr == ATTR_EXT)
 				continue;
@@ -379,6 +379,7 @@
 				fat_brelse(sb, bh);
 				return -ENOTEMPTY;
 			}
+		}
 		if (bh)
 			fat_brelse(sb, bh);
 	}
diff -u --recursive --new-file v2.0.35/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.0.35/linux/fs/nfs/dir.c	Sun Nov 15 10:49:49 1998
+++ linux/fs/nfs/dir.c	Sun Nov 15 10:33:14 1998
@@ -8,6 +8,11 @@
  * 10 Apr 1996	Added silly rename for unlink	--okir
  */
 
+/*
+ * Fixes:
+ *    Ion Badulescu <ionut@cs.columbia.edu>     : FIFO's need special handling in NFSv2
+ */
+
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
@@ -443,7 +448,10 @@
 		iput(dir);
 		return -ENAMETOOLONG;
 	}
-	sattr.mode = mode;
+	if (S_ISFIFO(mode))
+		sattr.mode = (mode & ~S_IFMT) | S_IFCHR;
+	else
+		sattr.mode = mode;
 	sattr.uid = sattr.gid = (unsigned) -1;
 	if (S_ISCHR(mode) || S_ISBLK(mode))
 		sattr.size = rdev; /* get out your barf bag */
@@ -452,6 +460,11 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
 		name, &sattr, &fhandle, &fattr);
+	if (error == -EINVAL && (S_ISFIFO(mode))) {
+		sattr.mode = mode;
+		error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+					name, &sattr, &fhandle, &fattr);
+	}
 	if (!error)
 	{
 		nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
diff -u --recursive --new-file v2.0.35/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.0.35/linux/fs/nfs/inode.c	Sat Nov 30 02:21:21 1996
+++ linux/fs/nfs/inode.c	Sun Nov 15 10:33:14 1998
@@ -344,6 +344,9 @@
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "nfsiod");
+#ifndef MODULE
+	current->blocked = ~0UL;
+#endif
 	ret = nfsiod();
 	MOD_DEC_USE_COUNT;
 	return ret;
diff -u --recursive --new-file v2.0.35/linux/fs/nfs/nfsiod.c linux/fs/nfs/nfsiod.c
--- v2.0.35/linux/fs/nfs/nfsiod.c	Sat Jun 29 02:00:46 1996
+++ linux/fs/nfs/nfsiod.c	Sun Nov 15 10:33:14 1998
@@ -99,10 +99,14 @@
 		/* Wait until user enqueues request */
 		dprintk("BIO: before: now %d nfsiod's active\n", active);
 		dprintk("BIO: nfsiod %d waiting\n", current->pid);
+#ifndef MODULE
+		current->signal = 0;
+#endif
 		interruptible_sleep_on(&req->rq_wait);
-
+#ifdef  MODULE
 		if (current->signal & ~current->blocked)
 			break;
+#endif
 		if (!req->rq_rpcreq.rq_slot)
 			continue;
 		dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n",
diff -u --recursive --new-file v2.0.35/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v2.0.35/linux/fs/nfs/nfsroot.c	Sun Nov 15 10:49:49 1998
+++ linux/fs/nfs/nfsroot.c	Sun Nov 15 10:33:14 1998
@@ -16,6 +16,7 @@
  *
  *	Changes:
  *
+ *	R. Drahtmueller	:	Set IFF_MULTICAST in dev->flags if applicable.
  *	Alan Cox	:	Removed get_address name clash with FPU.
  *	Alan Cox	:	Reformatted a bit.
  *	Gero Kuhlmann	:	Code cleanup
@@ -179,7 +180,11 @@
 		    (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
 			/* First up the interface */
 			old_flags = dev->flags;
+#ifdef CONFIG_IP_MULTICAST
+			dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
+#else
 			dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+#endif
 			if (!(old_flags & IFF_UP) && dev_open(dev)) {
 				dev->flags = old_flags;
 				continue;
diff -u --recursive --new-file v2.0.35/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v2.0.35/linux/fs/nfs/proc.c	Mon Jul 13 13:46:38 1998
+++ linux/fs/nfs/proc.c	Sun Nov 15 10:33:14 1998
@@ -24,6 +24,11 @@
  */
 
 /*
+ * Fixes:
+ *    Ion Badulescu <ionut@cs.columbia.edu>     : FIFO's need special handling in NFSv2
+ */
+
+/*
  * Defining NFS_PROC_DEBUG causes a lookup of a file named
  * "xyzzy" to toggle debugging.  Just cd to an NFS-mounted
  * filesystem and type 'ls xyzzy' to turn on debugging.
@@ -180,6 +185,11 @@
 	fattr->mtime.useconds = ntohl(*p++);
 	fattr->ctime.seconds = ntohl(*p++);
 	fattr->ctime.useconds = ntohl(*p++);
+	if (fattr->type == NFCHR && fattr->rdev == NFS_FIFO_DEV) {
+		fattr->type = NFFIFO;
+		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
+		fattr->rdev = 0;
+	}
 	return p;
 }
 
diff -u --recursive --new-file v2.0.35/linux/fs/pipe.c linux/fs/pipe.c
--- v2.0.35/linux/fs/pipe.c	Wed Aug  6 17:52:01 1997
+++ linux/fs/pipe.c	Sun Nov 15 10:33:14 1998
@@ -413,7 +413,7 @@
 	int error;
 	int i,j;
 
-	error = ENFILE;
+	error = -ENFILE;
 	f1 = get_empty_filp();
 	if (!f1)
 		goto no_files;
diff -u --recursive --new-file v2.0.35/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.0.35/linux/fs/proc/array.c	Sun Nov 15 10:49:49 1998
+++ linux/fs/proc/array.c	Sun Nov 15 10:33:14 1998
@@ -29,7 +29,7 @@
  * Yves Arrouye      :  remove removal of trailing spaces in get_array.
  *			<Yves.Arrouye@marin.fdn.fr>
  *
- * Alan Cox	     :  security fixes. <Alan.Cox@linux.org>a
+ * Alan Cox	     :  security fixes. <Alan.Cox@linux.org>
  */
 
 #include <linux/types.h>
@@ -1074,7 +1074,7 @@
 		case PROC_PID_CMDLINE:
 			return 0;	
 	}
-	if(current->fsuid == (*p)->euid)
+	if(suser() || current->fsuid == (*p)->euid)
 		return 0;
 	return 1;
 }
diff -u --recursive --new-file v2.0.35/linux/fs/proc/mem.c linux/fs/proc/mem.c
--- v2.0.35/linux/fs/proc/mem.c	Wed Sep 11 07:57:16 1996
+++ linux/fs/proc/mem.c	Sun Nov 15 10:33:14 1998
@@ -219,7 +219,7 @@
 	pgd_t *src_dir, *dest_dir;
 	pmd_t *src_middle, *dest_middle;
 	pte_t *src_table, *dest_table;
-	unsigned long stmp, dtmp;
+	unsigned long stmp, dtmp, mapnr;
 	struct vm_area_struct *src_vma = NULL;
 
 	/* Get the source's task information */
@@ -299,7 +299,9 @@
 
 		set_pte(src_table, pte_mkdirty(*src_table));
 		set_pte(dest_table, *src_table);
-		mem_map[MAP_NR(pte_page(*src_table))].count++;
+                mapnr = MAP_NR(pte_page(*src_table));
+		if (mapnr < MAP_NR(high_memory))
+                        mem_map[mapnr].count++;
 
 		stmp += PAGE_SIZE;
 		dtmp += PAGE_SIZE;
diff -u --recursive --new-file v2.0.35/linux/fs/read_write.c linux/fs/read_write.c
--- v2.0.35/linux/fs/read_write.c	Tue Aug 20 23:18:09 1996
+++ linux/fs/read_write.c	Sun Nov 15 10:33:14 1998
@@ -155,7 +155,12 @@
 	if (!file->f_op || !file->f_op->write)
 		goto out;
 	error = 0;
-	if (!count)
+	/*
+	 *	If this was a development kernel we'd just drop the test
+	 *	its not so we do this for stricter compatibility both to
+	 *	applications and drivers.
+	 */
+	if (!count && !IS_ZERO_WR(inode))
 		goto out;
 	error = locks_verify_area(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count);
 	if (error)
@@ -268,6 +273,10 @@
 	fn = file->f_op->read;
 	if (type == VERIFY_READ)
 		fn = (IO_fn_t) file->f_op->write;		
+		
+	if(fn==NULL)
+		return -EOPNOTSUPP;
+		
 	vector = iov;
 	while (count > 0) {
 		void * base;
diff -u --recursive --new-file v2.0.35/linux/fs/select.c linux/fs/select.c
--- v2.0.35/linux/fs/select.c	Mon Jul 13 13:46:38 1998
+++ linux/fs/select.c	Sun Nov 15 10:33:14 1998
@@ -46,7 +46,7 @@
  * Linus noticed.  -- jrs
  */
 
-static void free_wait(select_table * p)
+void select_free_wait(select_table * p)
 {
 	struct select_table_entry * entry = p->entry + p->nr;
 
@@ -96,7 +96,7 @@
  * and we aren't going to sleep on the select_table.  -- jrs
  */
 
-static int check(int flag, select_table * wait, struct file * file)
+int select_check(int flag, select_table * wait, struct file * file)
 {
 	struct inode * inode;
 	struct file_operations *fops;
@@ -180,17 +180,17 @@
 		struct file * file = current->files->fd[i];
 		if (!file)
 			continue;
-		if (FD_ISSET(i,in) && check(SEL_IN,wait,file)) {
+		if (FD_ISSET(i,in) && select_check(SEL_IN,wait,file)) {
 			FD_SET(i, res_in);
 			count++;
 			wait = NULL;
 		}
-		if (FD_ISSET(i,out) && check(SEL_OUT,wait,file)) {
+		if (FD_ISSET(i,out) && select_check(SEL_OUT,wait,file)) {
 			FD_SET(i, res_out);
 			count++;
 			wait = NULL;
 		}
-		if (FD_ISSET(i,ex) && check(SEL_EX,wait,file)) {
+		if (FD_ISSET(i,ex) && select_check(SEL_EX,wait,file)) {
 			FD_SET(i, res_ex);
 			count++;
 			wait = NULL;
@@ -201,7 +201,7 @@
 		schedule();
 		goto repeat;
 	}
-	free_wait(&wait_table);
+	select_free_wait(&wait_table);
 	free_page((unsigned long) entry);
 	current->state = TASK_RUNNING;
 bale:
diff -u --recursive --new-file v2.0.35/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- v2.0.35/linux/fs/smbfs/proc.c	Mon Jul 13 13:46:38 1998
+++ linux/fs/smbfs/proc.c	Sun Nov 15 10:33:14 1998
@@ -205,13 +205,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). */
@@ -1221,15 +1221,15 @@
 			WSET(param, 10, 8 + 4 + 2);	/* resume required +
 							   close on end +
 							   continue */
+		}
 #ifdef CONFIG_SMB_WIN95
-			/* Windows 95 is not able to deliver answers
-			   to FIND_NEXT fast enough, so sleep 0.2 seconds */
-			current->timeout = jiffies + HZ / 5;
-			current->state = TASK_INTERRUPTIBLE;
-			schedule();
-			current->timeout = 0;
+		/* Windows 95 is not able to deliver answers
+		   to FIND_NEXT fast enough, so sleep 0.2 seconds */
+		current->timeout = jiffies + HZ / 5;
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		current->timeout = 0;
 #endif
-		}
 
 		result = smb_trans2_request(server, command,
 					    0, NULL, 12 + mask_len + 2, param,
diff -u --recursive --new-file v2.0.35/linux/fs/super.c linux/fs/super.c
--- v2.0.35/linux/fs/super.c	Sun Nov 15 10:49:49 1998
+++ linux/fs/super.c	Sun Nov 15 10:33:15 1998
@@ -973,7 +973,9 @@
 
 #ifdef CONFIG_BLK_DEV_FD
 	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
+#ifdef CONFIG_BLK_DEV_INITRD
 		extern int rd_doload;
+#endif
 		floppy_eject();
 #ifndef CONFIG_BLK_DEV_RAM
 		printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n");
diff -u --recursive --new-file v2.0.35/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.0.35/linux/fs/vfat/namei.c	Mon Jul 13 13:46:39 1998
+++ linux/fs/vfat/namei.c	Sun Nov 15 10:33:15 1998
@@ -10,7 +10,6 @@
  *    the problem, send a script that demonstrates it.
  */
 
-#include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 
@@ -832,6 +831,9 @@
 		if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) {
 			return 0;
 		}
+		if (name_len == 2 && name[0] == '.' && name[1] == '.') {
+			return 0;
+		}
 	}
 
 	s1 = name; s2 = vf->name;
@@ -967,7 +969,6 @@
 		sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
 		sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
 		res = 0;
-		return 0;
 	} else {
 		res = -ENOENT;
 	}
diff -u --recursive --new-file v2.0.35/linux/include/asm-alpha/lca.h linux/include/asm-alpha/lca.h
--- v2.0.35/linux/include/asm-alpha/lca.h	Mon Jul 13 13:46:40 1998
+++ linux/include/asm-alpha/lca.h	Sun Nov 15 10:33:15 1998
@@ -1,3 +1,4 @@
+#include <linux/config.h> /* CONFIG_ALPHA_SRM_SETUP */
 #ifndef __ALPHA_LCA__H__
 #define __ALPHA_LCA__H__
 
diff -u --recursive --new-file v2.0.35/linux/include/asm-alpha/unistd.h linux/include/asm-alpha/unistd.h
--- v2.0.35/linux/include/asm-alpha/unistd.h	Thu Apr  3 20:00:00 1997
+++ linux/include/asm-alpha/unistd.h	Sun Nov 15 10:33:15 1998
@@ -274,8 +274,8 @@
 	return sys_write(fd, buf, nr);
 }
 
-extern int sys_read(int, char *, int);
-static inline int read(int fd, char * buf, int nr)
+extern int sys_read(unsigned int, char *, int);
+static inline int read(unsigned int fd, char * buf, int nr)
 {
 	return sys_read(fd, buf, nr);
 }
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- v2.0.35/linux/include/asm-i386/bugs.h	Tue Dec  2 13:52:32 1997
+++ linux/include/asm-i386/bugs.h	Sun Nov 15 10:33:15 1998
@@ -145,9 +145,192 @@
 	}
 }
 
+/*
+ *      B step AMD K6 before B 9730xxxx have hardware bugs that can cause
+ *      misexecution of code under Linux. Owners of such processors should
+ *      contact AMD for precise details and a (free) CPU exchange.
+ *
+ *      See     http://www.chorus.com/~poulot/k6bug.html
+ *              http://www.amd.com/K6/k6docs/revgd.html
+ *
+ *      The following test is erm... interesting. AMD neglected to up
+ *      the chip stepping when fixing the bug but they also tweaked some
+ *      performance at the same time...
+ */
+ 
+extern void vide(void);
+__asm__(".align 4\nvide: ret");
+
+static void check_k6_bug(void)
+{
+
+        if ((strcmp(x86_vendor_id, "AuthenticAMD") == 0) &&
+            (x86_model == 6) && (x86_mask == 1))
+        {
+		int n;
+                void (*f_vide)(void);
+                unsigned long d, d2;
+
+                printk(KERN_INFO "AMD K6 stepping B detected - ");
+
+#define K6_BUG_LOOP 1000000
+
+                /*
+                 * It looks like AMD fixed the 2.6.2 bug and improved indirect 
+                 * calls at the same time.
+                 */
+
+                n = K6_BUG_LOOP;
+                f_vide = vide;
+                __asm__ ("rdtsc" : "=a" (d));
+                while (n--) 
+                        f_vide();
+                __asm__ ("rdtsc" : "=a" (d2));
+                d = d2-d;
+
+                if (d > 20*K6_BUG_LOOP) {
+                        printk("system stability may be impaired when more than 32 MB are used.\n");
+		}
+                else 
+                        printk("probably OK (after B9730xxxx).\n");
+        }
+}
+
+/* Cyrix stuff from this point on */
+
+/* Cyrix 5/2 test (return 0x200 if it's a Cyrix) */
+static inline int test_cyrix_52div(void)
+{
+	int test;
+
+	__asm__ __volatile__("xor %%eax,%%eax\n\t"
+	     "sahf\n\t"
+	     "movb $5,%%al\n\t"
+	     "movb $2,%%bl\n\t"
+	     "div %%bl\n\t"
+	     "lahf\n\t"
+	     "andl $0xff00,%%eax": "=eax" (test) : : "bx");
+
+	return test;
+}
+
+/* test for CCR3 bit 7 r/w */
+static char test_cyrix_cr3rw(void)
+{
+	char temp, test;
+	
+	temp = getCx86(CX86_CCR3);	/* get current CCR3 value */
+	setCx86(CX86_CCR3, temp ^ 0x80); /* toggle test bit and write */
+	getCx86(0xc0);			/* dummy to change bus */
+	test = temp - getCx86(CX86_CCR3);	/* != 0 if ccr3 r/w */
+	setCx86(CX86_CCR3, temp);	/* return CCR3 to original value */
+	
+	return test;
+}
+
+/* redo the cpuid test in head.S, so that those 6x86(L) now get
+   detected properly (0 == no cpuid) */
+static inline int test_cpuid(void)
+{
+	int test;
+	
+	__asm__("pushfl\n\t"
+	     "popl %%eax\n\t"
+	     "movl %%eax,%%ecx\n\t"
+	     "xorl $0x200000,%%eax\n\t"
+	     "pushl %%eax\n\t"
+	     "popfl\n\t"
+	     "pushfl\n\t"
+	     "popl %%eax\n\t"
+	     "xorl %%ecx,%%eax\n\t"
+	     "pushl %%ecx\n\t"
+	     "popfl" : "=eax" (test) : : "cx");
+
+	return test;
+}
+
+/* All Cyrix 6x86 and 6x86L need the SLOP bit reset so that the udelay loop
+ * calibration works well.
+ * This routine must be called with MAPEN enabled, otherwise we don't
+ * have access to CCR5.
+ */
+ 
+static void check_6x86_slop(void)
+{
+	if (x86_model == 2)	/* if 6x86 or 6x86L */
+		setCx86(CX86_CCR5, getCx86(CX86_CCR5) & 0xfd); /* reset SLOP */
+}
+
+/* Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
+ * by the fact that they preserve the flags across the division of 5/2.
+ * PII and PPro exhibit this behavior too, but they have cpuid available.
+ */
+ 
+static void check_cyrix_various(void)
+{
+	if ((x86 == 4) && (test_cyrix_52div()==0x200)) 
+	{
+		/* if it's a Cyrix */
+	    
+		unsigned long flags;
+
+		/* default to an "old" Cx486 */
+		strcpy(x86_vendor_id, "CyrixInstead");
+		x86_model = -1;
+		x86_mask = 0;
+	    
+		/* Disable interrupts */
+		save_flags(flags);
+		cli();
+			
+		/* First check for very old CX486 models */
+		/* that did not have DIR0/DIR1. */
+		if (test_cyrix_cr3rw()) 
+		{	/* if has DIR0/DIR1 */
+		
+			char ccr3;
+			char dir0;
+			x86_model = 0;
+
+			/* Enable MAPEN */
+			ccr3 = getCx86(CX86_CCR3);
+			setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
+
+			dir0 = getCx86(CX86_DIR0);
+			if ((dir0 & 0xf0) == 0x30)	/* Use DIR0 to determine if this is a 6x86 class processor */
+			{
+				/* try enabling cpuid */
+				setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80);
+			}
+
+			if (test_cpuid()) 
+			{
+				int eax, dummy;
+
+				/* get processor info */
+			
+				cpuid(1, &eax, &dummy, &dummy,
+				      &x86_capability);
+
+				have_cpuid = 1;
+				x86_model = (eax >> 4) & 0xf;
+				x86 = (eax >> 8) & 0xf;
+				check_6x86_slop();
+			}
+			/* disable MAPEN */
+			setCx86(CX86_CCR3, ccr3);
+		} /* endif has DIR0/DIR1 */
+		sti();
+		restore_flags(flags);	/* restore interrupt state */
+	} /* endif it's a Cyrix */
+}
+
+/* Check various processor bugs */
 
 static void check_bugs(void)
 {
+	check_cyrix_various();
+	check_k6_bug();
 	check_tlb();
 	check_fpu();
 	check_hlt();
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.0.35/linux/include/asm-i386/irq.h	Mon Jul 13 13:46:41 1998
+++ linux/include/asm-i386/irq.h	Sun Nov 15 10:33:15 1998
@@ -108,6 +108,17 @@
 	"1:\tjmp 1f\n" \
 	"1:\toutb %al,$0x20\n\t"
 
+/* do not modify the ISR nor the cache_A1 variable */
+#define MSGACK_SECOND(mask,nr) \
+	"inb $0xA1,%al\n\t" \
+	"jmp 1f\n" \
+	"1:\tjmp 1f\n" \
+	"1:\tmovb $0x20,%al\n\t" \
+	"outb %al,$0xA0\n\t" \
+	"jmp 1f\n" \
+	"1:\tjmp 1f\n" \
+	"1:\toutb %al,$0x20\n\t"
+
 #define UNBLK_FIRST(mask) \
 	"inb $0x21,%al\n\t" \
 	"jmp 1f\n" \
@@ -162,6 +173,8 @@
 	"jnc 3f\n\t" \
 	"cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \
 	"je 4f\n\t" \
+	"cmpb "SYMBOL_NAME_STR(boot_cpu_id)", %al\n\t" \
+	"jne 2f\n\t" \
 	"movb $1, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \
 	"2: " \
         SMP_PROF_INT_SPINS \
@@ -190,7 +203,10 @@
 	"movb %al, "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
 	"4: " \
 	"incl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \
+	"cmpb "SYMBOL_NAME_STR(boot_cpu_id)", %al\n\t" \
+	"jne 7f\n\t" \
 	"movb $0, "SYMBOL_NAME_STR(smp_blocked_interrupt_pending)"\n\t" \
+	"7: " \
 	"popfl\n\t" \
 	"popl %edx\n\t" \
 	"popl %ecx\n\t" \
@@ -302,34 +318,14 @@
 __asm__( \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
-	"pushl $-"#nr"-2\n\t" \
-	SAVE_ALL \
-	ENTER_KERNEL \
-	ACK_##chip(mask,(nr&7)) \
-	"incl "SYMBOL_NAME_STR(intr_count)"\n\t"\
-	"sti\n\t" \
-	"movl %esp,%ebx\n\t" \
-	"pushl %ebx\n\t" \
-	"pushl $" #nr "\n\t" \
-	"call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
-	"addl $8,%esp\n\t" \
-	"cli\n\t" \
-	UNBLK_##chip(mask) \
-	GET_PROCESSOR_ID \
-	"btrl $" STR(SMP_FROM_INT) ","SYMBOL_NAME_STR(smp_proc_in_lock)"(,%eax,4)\n\t" \
-	"decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
-	"incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
-	"jmp ret_from_sys_call\n" \
-"\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
 	SAVE_MOST \
-	ACK_##chip(mask,(nr&7)) \
+	MSGACK_##chip(mask,(nr&7)) \
 	SMP_PROF_IPI_CNT \
 	"pushl $" #nr "\n\t" \
 	"call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \
 	"addl $4,%esp\n\t" \
 	"cli\n\t" \
-	UNBLK_##chip(mask) \
 	RESTORE_MOST \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/mtrr.h linux/include/asm-i386/mtrr.h
--- v2.0.35/linux/include/asm-i386/mtrr.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/mtrr.h	Sun Nov 15 10:33:15 1998
@@ -0,0 +1,65 @@
+/*  Generic MTRR (Memory Type Range Register) ioctls.
+
+    Copyright (C) 1997  Richard Gooch
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
+    The postal address is:
+      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+
+    modified by Mathias Fr"ohlich, Jan, 1998
+    <frohlich@na.uni-tuebingen.de>
+*/
+#ifndef _LINUX_MTRR_H
+#define _LINUX_MTRR_H
+
+/*  These are the region types  */
+#define MTRR_TYPE_UNCACHABLE 0
+#define MTRR_TYPE_WRCOMB     1
+/*#define MTRR_TYPE_         2*/
+/*#define MTRR_TYPE_         3*/
+#define MTRR_TYPE_WRTHROUGH  4
+#define MTRR_TYPE_WRPROT     5
+#define MTRR_TYPE_WRBACK     6
+#define MTRR_NUM_TYPES       7
+
+static char *attrib_to_str (int x) __attribute__ ((unused));
+
+static char *attrib_to_str (int x)
+{
+	switch (x) {
+	case 0: return "uncachable";
+	case 1: return "write-combining";
+	case 4: return "write-through";
+	case 5: return "write-protect";
+	case 6: return "write-back";
+	default: return "?";
+	}
+}   /*  End Function attrib_to_str  */
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_MTRR
+
+extern void check_mtrr_config(void);
+extern void init_mtrr_config(void);
+/* extern void set_mtrr_config(void); */
+
+#endif /* CONFIG_MTRR */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_MTRR_H */
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.0.35/linux/include/asm-i386/processor.h	Mon Jul 13 13:46:41 1998
+++ linux/include/asm-i386/processor.h	Sun Nov 15 10:33:15 1998
@@ -27,6 +27,53 @@
 extern char hlt_works_ok;	/* problems on some 486Dx4's and old 386's */
 extern int  have_cpuid;		/* We have a CPUID */
 
+extern unsigned long cpu_hz;	/* CPU clock frequency from time.c */
+
+/*
+ *	Detection of CPU model (CPUID).
+ */
+extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx)
+{
+	__asm__("cpuid"
+		: "=a" (*eax),
+		  "=b" (*ebx),
+		  "=c" (*ecx),
+		  "=d" (*edx)
+		: "a" (op)
+		: "cc");
+}
+
+/*
+ * Cyrix CPU register indexes (use special macros to access these)
+ */
+#define CX86_CCR2 0xc2
+#define CX86_CCR3 0xc3
+#define CX86_CCR4 0xe8
+#define CX86_CCR5 0xe9
+#define CX86_DIR0 0xfe
+#define CX86_DIR1 0xff
+
+/*
+ * Cyrix CPU register access macros
+ */
+
+extern inline unsigned char getCx86(unsigned char reg)
+{
+	unsigned char data;
+
+	__asm__ __volatile__("movb %1,%%al\n\t"
+		      "outb %%al,$0x22\n\t"
+		      "inb $0x23,%%al" : "=a" (data) : "q" (reg));
+	return data;
+}
+
+extern inline void setCx86(unsigned char reg, unsigned char data)
+{
+	__asm__ __volatile__("outb %%al,$0x22\n\t"
+	     "movb %1,%%al\n\t"
+	     "outb %%al,$0x23" : : "a" (reg), "q" (data));
+}
+
 /*
  * Bus types (default is ISA, but people can check others with these..)
  * MCA_bus hardcoded to 0 for now.
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/ptrace.h linux/include/asm-i386/ptrace.h
--- v2.0.35/linux/include/asm-i386/ptrace.h	Sun Nov 15 10:49:51 1998
+++ linux/include/asm-i386/ptrace.h	Sun Nov 15 10:33:16 1998
@@ -53,6 +53,8 @@
 #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs))
 #define instruction_pointer(regs) ((regs)->eip)
 extern void show_regs(struct pt_regs *);
+struct task_struct;
+extern void get_pt_regs_for_task(struct pt_regs *, struct task_struct *p);
 #endif
 
 #endif
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v2.0.35/linux/include/asm-i386/smp.h	Fri Apr 26 00:37:21 1996
+++ linux/include/asm-i386/smp.h	Sun Nov 15 10:33:16 1998
@@ -160,6 +160,7 @@
 	char x86_mask;
 	char x86_vendor_id[16];
 	int  x86_capability;
+	int  x86_ext_capability;
 	int  fdiv_bug;
 	int  have_cpuid;
 	char wp_works_ok;
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v2.0.35/linux/include/asm-i386/system.h	Mon Mar 17 15:00:50 1997
+++ linux/include/asm-i386/system.h	Sun Nov 15 10:33:16 1998
@@ -296,4 +296,39 @@
 void disable_hlt(void);
 void enable_hlt(void);
 
+static __inline__ unsigned long long rdmsr(unsigned int msr)
+{
+	unsigned long long ret;
+        __asm__ __volatile__("rdmsr"
+			    : "=A" (ret)
+			    : "c" (msr));
+        return ret;
+}
+
+static __inline__ void wrmsr(unsigned int msr,unsigned long long val)
+{
+        __asm__ __volatile__("wrmsr"
+			    : /* no Outputs */
+			    : "c" (msr), "A" (val));
+}
+
+
+static __inline__ unsigned long long rdtsc(void)
+{
+	unsigned long long ret;
+        __asm__ __volatile__("rdtsc"
+			    : "=A" (ret)
+			    : /* no inputs */);
+        return ret;
+}
+
+static __inline__ unsigned long long rdpmc(unsigned int counter)
+{
+	unsigned long long ret;
+        __asm__ __volatile__("rdpmc"
+			    : "=A" (ret)
+			    : "c" (counter));
+        return ret;
+}
+
 #endif
diff -u --recursive --new-file v2.0.35/linux/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h
--- v2.0.35/linux/include/asm-i386/unistd.h	Thu Mar 21 22:34:02 1996
+++ linux/include/asm-i386/unistd.h	Sun Nov 15 10:33:16 1998
@@ -169,6 +169,9 @@
 #define __NR_sched_rr_get_interval	161
 #define __NR_nanosleep		162
 #define __NR_mremap		163
+#define __NR_poll		168
+#define __NR_getpmsg		188
+#define __NR_putpmsg		189
 
 /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
 #define _syscall0(type,name) \
@@ -275,6 +278,8 @@
 static inline _syscall0(int,setup)
 static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
+static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall1(int,dup,int,fd)
 static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
diff -u --recursive --new-file v2.0.35/linux/include/asm-mips/ioctl.h linux/include/asm-mips/ioctl.h
--- v2.0.35/linux/include/asm-mips/ioctl.h	Wed Dec 13 02:39:45 1995
+++ linux/include/asm-mips/ioctl.h	Sun Nov 15 10:33:16 1998
@@ -11,7 +11,7 @@
  * the process. I'd like to clean it up for the i386 as well, but
  * it's so painful recognizing both the new and the old numbers..
  *
- * The same applies for for the MIPS ABI; in fact even the macros
+ * The same applies for the MIPS ABI; in fact even the macros
  * from Linux/Alpha fit almost perfectly.
  */
 
diff -u --recursive --new-file v2.0.35/linux/include/asm-mips/mipsregs.h linux/include/asm-mips/mipsregs.h
--- v2.0.35/linux/include/asm-mips/mipsregs.h	Wed Dec 13 02:39:46 1995
+++ linux/include/asm-mips/mipsregs.h	Sun Nov 15 10:33:16 1998
@@ -238,7 +238,7 @@
 /*
  * Bitfields and bit numbers in the coprocessor 0 cause register.
  *
- * Refer to to your MIPS R4xx0 manual, chapter 5 for explanation.
+ * Refer to your MIPS R4xx0 manual, chapter 5 for explanation.
  */
 #define  CAUSEB_EXCCODE		2
 #define  CAUSEF_EXCCODE		(31  <<  2)
diff -u --recursive --new-file v2.0.35/linux/include/linux/apm_bios.h linux/include/linux/apm_bios.h
--- v2.0.35/linux/include/linux/apm_bios.h	Wed Oct 15 15:22:05 1997
+++ linux/include/linux/apm_bios.h	Sun Nov 15 10:33:16 1998
@@ -83,7 +83,7 @@
 extern int		apm_register_callback(int (*callback)(apm_event_t));
 extern void		apm_unregister_callback(int (*callback)(apm_event_t));
 
-extern int		apm_set_power_state(ushort state);
+extern void		apm_power_off(void);
 extern int		apm_display_blank(void);
 extern int		apm_display_unblank(void);
 
diff -u --recursive --new-file v2.0.35/linux/include/linux/b1lli.h linux/include/linux/b1lli.h
--- v2.0.35/linux/include/linux/b1lli.h	Mon Aug  4 17:34:01 1997
+++ linux/include/linux/b1lli.h	Sun Nov 15 10:33:17 1998
@@ -1,11 +1,44 @@
 /*
- * $Id: b1lli.h,v 1.1 1997/03/04 21:27:32 calle Exp $
+ * $Id: b1lli.h,v 1.1.2.10 1998/10/25 14:37:35 fritz 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.2.10  1998/10/25 14:37:35  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.1.2.9  1998/03/20 14:30:02  calle
+ * added cardnr to detect if you try to add same T1 to different io address.
+ * change number of nccis depending on number of channels.
+ *
+ * Revision 1.1.2.8  1998/03/04 17:32:33  calle
+ * Changes for T1.
+ *
+ * Revision 1.1.2.7  1998/02/27 15:38:29  calle
+ * T1 running with slow link.
+ *
+ * Revision 1.1.2.6  1998/02/24 17:57:36  calle
+ * changes for T1.
+ *
+ * Revision 1.1.2.5  1998/01/27 16:11:50  calle
+ * support for PCMCIA B1/M1/M2 ready.
+ *
+ * Revision 1.1.2.4  1998/01/26 14:51:56  calle
+ * interface change for pcmcia cards.
+ *
+ * Revision 1.1.2.3  1998/01/23 16:46:45  calle
+ * new functions for pcmcia cards.
+ *
+ * Revision 1.1.2.2  1997/11/26 16:57:26  calle
+ * more changes for B1/M1/T1.
+ *
+ * Revision 1.1.2.1  1997/11/26 10:47:01  calle
+ * prepared for M1 (Mobile) and T1 (PMX) cards.
+ * prepared to set configuration after load to support other D-channel
+ * protocols, point-to-point and leased lines.
+ *
  * Revision 1.1  1997/03/04 21:27:32  calle
  * First version in isdn4linux
  *
@@ -32,10 +65,22 @@
 	avmb1_t4file t4file;
 } avmb1_loaddef;
 
+typedef struct avmb1_loadandconfigdef {
+	int contr;
+	avmb1_t4file t4file;
+        avmb1_t4file t4config; 
+} avmb1_loadandconfigdef;
+
 typedef struct avmb1_resetdef {
 	int contr;
 } avmb1_resetdef;
 
+typedef struct avmb1_getdef {
+	int contr;
+	int cardtype;
+	int cardstate;
+} avmb1_getdef;
+
 /*
  * struct for adding new cards 
  */
@@ -44,33 +89,47 @@
 	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 */
+#define AVM_CARDTYPE_B1	0
+#define AVM_CARDTYPE_T1	1
+#define AVM_CARDTYPE_M1	2
+#define AVM_CARDTYPE_M2	3
 
+typedef struct avmb1_extcarddef {
+	int port;
+	int irq;
+        int cardtype;
+        int cardnr;  /* for HEMA/T1 */
+} avmb1_extcarddef;
+
+#define	AVMB1_LOAD		0	/* load image to card */
+#define AVMB1_ADDCARD		1	/* add a new card */
+#define AVMB1_RESETCARD		2	/* reset a card */
+#define	AVMB1_LOAD_AND_CONFIG	3	/* load image and config to card */
+#define	AVMB1_ADDCARD_WITH_TYPE	4	/* add a new card, with cardtype */
+#define AVMB1_GET_CARDINFO	5	/* get cardtype */
+#define AVMB1_REMOVECARD	6	/* remove a card (usefull for T1) */
 
 
-#ifdef __KERNEL__
 
 /*
  * card states for startup
  */
 
-#define CARD_NONE	0
+#define CARD_FREE	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
+#ifdef __KERNEL__
+
+#define	AVMB1_PORTLEN		0x1f
 
-#define AVM_MAXVERSION	8
-#define AVM_NBCHAN	2
+#define AVM_MAXVERSION		8
 
-#define AVM_NAPPS	30
-#define AVM_NPLCI	5
-#define AVM_NNCCI	6
+#define AVM_NAPPS		30
+#define AVM_NNCCI_PER_CHANNEL	4
 
 /*
  * Main driver data
@@ -79,8 +138,10 @@
 typedef struct avmb1_card {
 	struct avmb1_card *next;
 	int cnr;
-	unsigned short port;
+	unsigned int port;
 	unsigned irq;
+	int cardtype;
+	int cardnr; /* for T1-HEMA */
 	volatile unsigned short cardstate;
 	int interrupt;
 	int blocked;
@@ -108,22 +169,26 @@
 
 
 /* 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);
+int B1_detect(unsigned int base, int cardtype);
+int T1_detectandinit(unsigned int base, unsigned irq, int cardnr);
+void B1_reset(unsigned int base);
+void T1_reset(unsigned int base);
+int B1_load_t4file(unsigned int base, avmb1_t4file * t4file);
+int B1_load_config(unsigned int base, avmb1_t4file * config);
+int B1_loaded(unsigned int base);
+void B1_setinterrupt(unsigned int base, unsigned irq, int cardtype);
+unsigned char B1_disable_irq(unsigned int base);
+void T1_disable_irq(unsigned int base);
+int B1_valid_irq(unsigned irq, int cardtype);
+int B1_valid_port(unsigned port, int cardtype);
 void B1_handle_interrupt(avmb1_card * card);
-void B1_send_init(unsigned short port,
+void B1_send_init(unsigned int port,
 	    unsigned int napps, unsigned int nncci, unsigned int cardnr);
-void B1_send_register(unsigned short port,
+void B1_send_register(unsigned int 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);
+void B1_send_release(unsigned int port, __u16 appid);
+void B1_send_message(unsigned int port, struct sk_buff *skb);
 
 /* b1capi.c */
 void avmb1_handle_new_ncci(avmb1_card * card,
@@ -133,8 +198,16 @@
 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);
+/* standard calls, with check and allocation of resources */
+int avmb1_addcard(int port, int irq, int cardtype);
+int avmb1_probecard(int port, int irq, int cardtype);
+
+int avmb1_resetcard(int cardnr);
+
+/* calls for pcmcia driver */
+int avmb1_detectcard(int port, int irq, int cardtype);
+int avmb1_registercard(int port, int irq, int cardtype, int allocio);
+int avmb1_unregistercard(int cnr, int freeio);
 
 #endif				/* __KERNEL__ */
 
diff -u --recursive --new-file v2.0.35/linux/include/linux/cyclades.h linux/include/linux/cyclades.h
--- v2.0.35/linux/include/linux/cyclades.h	Sat Apr 20 11:09:56 1996
+++ linux/include/linux/cyclades.h	Sun Nov 15 10:33:17 1998
@@ -1,14 +1,54 @@
-/*
+/* $Revision: 2.6 $$Date: 1998/08/10 16:57:01 $
  * linux/include/linux/cyclades.h
  *
- * This file is maintained by Marcio Saito <marcio@cyclades.com> and
+ * This file is maintained by Ivan Passos <ivan@cyclades.com>, 
+ * Marcio Saito <marcio@cyclades.com> and
  * Randolph Bentson <bentson@grieg.seaslug.org>.
  *
  * This file contains the general definitions for the cyclades.c driver
  *$Log: cyclades.h,v $
- * Revision 1.5  1995/11/13  21:13:31  bentson
- * changes suggested by Michael Chastain <mec@duracef.shout.net>
- * to support use of this file in non-kernel applications
+ *Revision 2.5  1998/08/03 16:57:01  ivan
+ *added cyclades_idle_stats structure;
+ * 
+ *Revision 2.4  1998/06/01 12:09:53  ivan
+ *removed closing_wait2 from cyclades_port structure;
+ *
+ *Revision 2.3  1998/03/16 18:01:12  ivan
+ *changes in the cyclades_port structure to get it closer to the 
+ *standard serial port structure;
+ *added constants for new ioctls;
+ *
+ *Revision 2.2  1998/02/17 16:50:00  ivan
+ *changes in the cyclades_port structure (addition of shutdown_wait and 
+ *chip_rev variables);
+ *added constants for new ioctls and for CD1400 rev. numbers.
+ *
+ *Revision 2.1	1997/10/24 16:03:00  ivan
+ *added rflow (which allows enabling the CD1400 special flow control 
+ *feature) and rtsdtr_inv (which allows DTR/RTS pin inversion) to 
+ *cyclades_port structure;
+ *added Alpha support
+ *
+ *Revision 2.0  1997/06/30 10:30:00  ivan
+ *added some new doorbell command constants related to IOCTLW and
+ *UART error signaling
+ *
+ *Revision 1.8  1997/06/03 15:30:00  ivan
+ *added constant ZFIRM_HLT
+ *added constant CyPCI_Ze_win ( = 2 * Cy_PCI_Zwin)
+ *
+ *Revision 1.7  1997/03/26 10:30:00  daniel
+ *new entries at the end of cyclades_port struct to reallocate
+ *variables illegally allocated within card memory.
+ *
+ *Revision 1.6  1996/09/09 18:35:30  bentson
+ *fold in changes for Cyclom-Z -- including structures for
+ *communicating with board as well modest changes to original
+ *structures to support new features.
+ *
+ *Revision 1.5  1995/11/13 21:13:31  bentson
+ *changes suggested by Michael Chastain <mec@duracef.shout.net>
+ *to support use of this file in non-kernel applications
  *
  *
  */
@@ -23,6 +63,22 @@
         unsigned long           char_last;
 };
 
+/*
+ * These stats all reflect activity since the device was last initialized.
+ * (i.e., since the port was opened with no other processes already having it
+ * open)
+ */
+struct cyclades_idle_stats {
+    time_t	   in_use;	/* Time device has been in use (secs) */
+    time_t	   recv_idle;	/* Time since last char received (secs) */
+    time_t	   xmit_idle;	/* Time since last char transmitted (secs) */
+    unsigned long  recv_bytes;	/* Bytes received */
+    unsigned long  xmit_bytes;	/* Bytes transmitted */
+    unsigned long  overruns;	/* Input overruns */
+    unsigned long  frame_errs;	/* Input framing errors */
+    unsigned long  parity_errs;	/* Input parity errors */
+};
+
 #define CYCLADES_MAGIC  0x4359
 
 #define CYGETMON                0x435901
@@ -34,17 +90,412 @@
 #define CYSETTIMEOUT            0x435907
 #define CYGETDEFTIMEOUT         0x435908
 #define CYSETDEFTIMEOUT         0x435909
+#define CYSETRFLOW		0x43590a
+#define CYGETRFLOW		0x43590b
+#define CYSETRTSDTR_INV		0x43590c
+#define CYGETRTSDTR_INV		0x43590d
+#define CYZSETPOLLCYCLE		0x43590e
+#define CYZGETPOLLCYCLE		0x43590f
+#define CYGETCD1400VER		0x435910
+#define CYGETCARDINFO		0x435911
+#define	CYSETWAIT		0x435912
+#define	CYGETWAIT		0x435913
+
+/*************** CYCLOM-Z ADDITIONS ***************/
+
+#define CZIOC           ('M' << 8)
+#define CZ_NBOARDS      (CZIOC|0xfa)
+#define CZ_BOOT_START   (CZIOC|0xfb)
+#define CZ_BOOT_DATA    (CZIOC|0xfc)
+#define CZ_BOOT_END     (CZIOC|0xfd)
+#define CZ_TEST         (CZIOC|0xfe)
+
+#define CZ_DEF_POLL	(HZ/25)
+
+#define MAX_BOARD       4       /* Max number of boards */
+#define MAX_DEV         256     /* Max number of ports total */
+#define	CYZ_MAX_SPEED	921600
+
+#define	CYZ_FIFO_SIZE	16
+
+#define CYZ_BOOT_NWORDS 0x100
+struct CYZ_BOOT_CTRL {
+        unsigned short  nboard;
+        int             status[MAX_BOARD];
+        int             nchannel[MAX_BOARD];
+        int             fw_rev[MAX_BOARD];
+        unsigned long   offset;
+        unsigned long   data[CYZ_BOOT_NWORDS];
+};
+
+
+#ifndef DP_WINDOW_SIZE
+/* #include "cyclomz.h" */
+/****************** ****************** *******************/
+/*
+ *	The data types defined below are used in all ZFIRM interface
+ *	data structures. They accomodate differences between HW
+ *	architectures and compilers.
+ */
+
+#if defined(__alpha__)
+typedef unsigned long	ucdouble;	/* 64 bits, unsigned */
+typedef unsigned int	uclong;		/* 32 bits, unsigned */
+#else
+typedef unsigned long	uclong;		/* 32 bits, unsigned */
+#endif
+typedef unsigned short	ucshort;	/* 16 bits, unsigned */
+typedef unsigned char	ucchar;		/* 8 bits, unsigned */
+
+/*
+ *	Memory Window Sizes
+ */
+
+#define	DP_WINDOW_SIZE		(0x00080000)	/* window size 512 Kb */
+#define	ZE_DP_WINDOW_SIZE	(0x00100000)	/* window size 1 Mb (Ze and
+						  8Zo V.2 */
+#define	CTRL_WINDOW_SIZE	(0x00000080)	/* runtime regs 128 bytes */
+
+/*
+ *	CUSTOM_REG - Cyclom-Z/PCI Custom Registers Set. The driver
+ *	normally will access only interested on the fpga_id, fpga_version,
+ *	start_cpu and stop_cpu.
+ */
+
+struct	CUSTOM_REG {
+	uclong	fpga_id;		/* FPGA Identification Register */
+	uclong	fpga_version;		/* FPGA Version Number Register */
+	uclong	cpu_start;		/* CPU start Register (write) */
+	uclong	cpu_stop;		/* CPU stop Register (write) */
+	uclong	misc_reg;		/* Miscelaneous Register */
+	uclong	idt_mode;		/* IDT mode Register */
+	uclong	uart_irq_status;	/* UART IRQ status Register */
+	uclong	clear_timer0_irq;	/* Clear timer interrupt Register */
+	uclong	clear_timer1_irq;	/* Clear timer interrupt Register */
+	uclong	clear_timer2_irq;	/* Clear timer interrupt Register */
+	uclong	test_register;		/* Test Register */
+	uclong	test_count;		/* Test Count Register */
+	uclong	timer_select;		/* Timer select register */
+	uclong	pr_uart_irq_status;	/* Prioritized UART IRQ stat Reg */
+	uclong	ram_wait_state;		/* RAM wait-state Register */
+	uclong	uart_wait_state;	/* UART wait-state Register */
+	uclong	timer_wait_state;	/* timer wait-state Register */
+	uclong	ack_wait_state;		/* ACK wait State Register */
+};
+
+/*
+ *	RUNTIME_9060 - PLX PCI9060ES local configuration and shared runtime
+ *	registers. This structure can be used to access the 9060 registers
+ *	(memory mapped).
+ */
+
+struct RUNTIME_9060 {
+	uclong	loc_addr_range;	/* 00h - Local Address Range */
+	uclong	loc_addr_base;	/* 04h - Local Address Base */
+	uclong	loc_arbitr;	/* 08h - Local Arbitration */
+	uclong	endian_descr;	/* 0Ch - Big/Little Endian Descriptor */
+	uclong	loc_rom_range;	/* 10h - Local ROM Range */
+	uclong	loc_rom_base;	/* 14h - Local ROM Base */
+	uclong	loc_bus_descr;	/* 18h - Local Bus descriptor */
+	uclong	loc_range_mst;	/* 1Ch - Local Range for Master to PCI */
+	uclong	loc_base_mst;	/* 20h - Local Base for Master PCI */
+	uclong	loc_range_io;	/* 24h - Local Range for Master IO */
+	uclong	pci_base_mst;	/* 28h - PCI Base for Master PCI */
+	uclong	pci_conf_io;	/* 2Ch - PCI configuration for Master IO */
+	uclong	filler1;	/* 30h */
+	uclong	filler2;	/* 34h */
+	uclong	filler3;	/* 38h */
+	uclong	filler4;	/* 3Ch */
+	uclong	mail_box_0;	/* 40h - Mail Box 0 */
+	uclong	mail_box_1;	/* 44h - Mail Box 1 */
+	uclong	mail_box_2;	/* 48h - Mail Box 2 */
+	uclong	mail_box_3;	/* 4Ch - Mail Box 3 */
+	uclong	filler5;	/* 50h */
+	uclong	filler6;	/* 54h */
+	uclong	filler7;	/* 58h */
+	uclong	filler8;	/* 5Ch */
+	uclong	pci_doorbell;	/* 60h - PCI to Local Doorbell */
+	uclong	loc_doorbell;	/* 64h - Local to PCI Doorbell */
+	uclong	intr_ctrl_stat;	/* 68h - Interrupt Control/Status */
+	uclong	init_ctrl;	/* 6Ch - EEPROM control, Init Control, etc */
+};
+
+/* Values for the Local Base Address re-map register */
+
+#define	WIN_RAM		0x00000001L	/* set the sliding window to RAM */
+#define	WIN_CREG	0x14000001L	/* set the window to custom Registers */
+
+/* Values timer select registers */
+
+#define	TIMER_BY_1M	0x00		/* clock divided by 1M */
+#define	TIMER_BY_256K	0x01		/* clock divided by 256k */
+#define	TIMER_BY_128K	0x02		/* clock divided by 128k */
+#define	TIMER_BY_32K	0x03		/* clock divided by 32k */
+
+/****************** ****************** *******************/
+#endif
+
+#ifndef ZFIRM_ID
+/* #include "zfwint.h" */
+/****************** ****************** *******************/
+/*
+ *	This file contains the definitions for interfacing with the
+ *	Cyclom-Z ZFIRM Firmware.
+ */
+
+/* General Constant definitions */
+
+#define	MAX_CHAN	64		/* max number of channels per board */
+
+/* firmware id structure (set after boot) */
+
+#define ID_ADDRESS	0x00000180L	/* signature/pointer address */
+#define	ZFIRM_ID	0x5557465AL	/* ZFIRM/U signature */
+#define	ZFIRM_HLT	0x59505B5CL	/* ZFIRM needs external power supply */
+#define	ZFIRM_RST	0x56040674L	/* RST signal (due to FW reset) */
+
+#define	ZF_TINACT_DEF	1000		/* default inactivity timeout 
+					   (1000 ms) */
+#define	ZF_TINACT	ZF_TINACT_DEF
+
+struct	FIRM_ID {
+	uclong	signature;		/* ZFIRM/U signature */
+	uclong	zfwctrl_addr;		/* pointer to ZFW_CTRL structure */
+};
+
+/* Op. System id */
+
+#define	C_OS_LINUX	0x00000030	/* generic Linux system */
+
+/* channel op_mode */
+
+#define	C_CH_DISABLE	0x00000000	/* channel is disabled */
+#define	C_CH_TXENABLE	0x00000001	/* channel Tx enabled */
+#define	C_CH_RXENABLE	0x00000002	/* channel Rx enabled */
+#define	C_CH_ENABLE	0x00000003	/* channel Tx/Rx enabled */
+#define	C_CH_LOOPBACK	0x00000004	/* Loopback mode */
+
+/* comm_parity - parity */
+
+#define	C_PR_NONE	0x00000000	/* None */
+#define	C_PR_ODD	0x00000001	/* Odd */
+#define C_PR_EVEN	0x00000002	/* Even */
+#define C_PR_MARK	0x00000004	/* Mark */
+#define C_PR_SPACE	0x00000008	/* Space */
+#define C_PR_PARITY	0x000000ff
+
+#define	C_PR_DISCARD	0x00000100	/* discard char with frame/par error */
+#define C_PR_IGNORE	0x00000200	/* ignore frame/par error */
+
+/* comm_data_l - data length and stop bits */
+
+#define C_DL_CS5	0x00000001
+#define C_DL_CS6	0x00000002
+#define C_DL_CS7	0x00000004
+#define C_DL_CS8	0x00000008
+#define	C_DL_CS		0x0000000f
+#define C_DL_1STOP	0x00000010
+#define C_DL_15STOP	0x00000020
+#define C_DL_2STOP	0x00000040
+#define	C_DL_STOP	0x000000f0
+
+/* interrupt enabling/status */
+
+#define	C_IN_DISABLE	0x00000000	/* zero, disable interrupts */
+#define	C_IN_TXBEMPTY	0x00000001	/* tx buffer empty */
+#define	C_IN_TXLOWWM	0x00000002	/* tx buffer below LWM */
+#define	C_IN_RXHIWM	0x00000010	/* rx buffer above HWM */
+#define	C_IN_RXNNDT	0x00000020	/* rx no new data timeout */
+#define	C_IN_MDCD	0x00000100	/* modem DCD change */
+#define	C_IN_MDSR	0x00000200	/* modem DSR change */
+#define	C_IN_MRI	0x00000400	/* modem RI change */
+#define	C_IN_MCTS	0x00000800	/* modem CTS change */
+#define	C_IN_RXBRK	0x00001000	/* Break received */
+#define	C_IN_PR_ERROR	0x00002000	/* parity error */
+#define	C_IN_FR_ERROR	0x00004000	/* frame error */
+#define C_IN_OVR_ERROR  0x00008000      /* overrun error */
+#define C_IN_RXOFL	0x00010000      /* RX buffer overflow */
+#define C_IN_IOCTLW	0x00020000      /* I/O control w/ wait */
+ 
+/* flow control */
+
+#define	C_FL_OXX	0x00000001	/* output Xon/Xoff flow control */
+#define	C_FL_IXX	0x00000002	/* output Xon/Xoff flow control */
+#define C_FL_OIXANY	0x00000004	/* output Xon/Xoff (any xon) */
+#define	C_FL_SWFLOW	0x0000000f
+
+/* flow status */
+
+#define	C_FS_TXIDLE	0x00000000	/* no Tx data in the buffer or UART */
+#define	C_FS_SENDING	0x00000001	/* UART is sending data */
+#define	C_FS_SWFLOW	0x00000002	/* Tx is stopped by received Xoff */
+
+/* rs_control/rs_status RS-232 signals */
+
+#define	C_RS_DCD	0x00000100	/* CD */
+#define	C_RS_DSR	0x00000200	/* DSR */
+#define	C_RS_RI		0x00000400	/* RI */
+#define	C_RS_CTS	0x00000800	/* CTS */
+#define	C_RS_RTS	0x00000001	/* RTS */
+#define	C_RS_DTR	0x00000004	/* DTR */
+
+/* commands Host <-> Board */
+
+#define	C_CM_RESET	0x01		/* reset/flush buffers */
+#define	C_CM_IOCTL	0x02		/* re-read CH_CTRL */
+#define	C_CM_IOCTLW	0x03		/* re-read CH_CTRL, intr when done */
+#define	C_CM_IOCTLM	0x04		/* RS-232 outputs change */
+#define	C_CM_SENDXOFF	0x10		/* send Xoff */
+#define	C_CM_SENDXON	0x11		/* send Xon */
+#define C_CM_CLFLOW	0x12		/* Clear flow control (resume) */
+#define	C_CM_SENDBRK	0x41		/* send break */
+#define	C_CM_INTBACK	0x42		/* Interrupt back */
+#define	C_CM_SET_BREAK	0x43		/* Tx break on */
+#define	C_CM_CLR_BREAK	0x44		/* Tx break off */
+#define	C_CM_CMD_DONE	0x45		/* Previous command done */
+#define	C_CM_TINACT	0x51		/* set inactivity detection */
+#define	C_CM_IRQ_ENBL	0x52		/* enable generation of interrupts */
+#define	C_CM_IRQ_DSBL	0x53		/* disable generation of interrupts */
+#define	C_CM_ACK_ENBL	0x54		/* enable acknolowdged interrupt mode */
+#define	C_CM_ACK_DSBL	0x55		/* disable acknolowdged intr mode */
+#define	C_CM_FLUSH_RX	0x56		/* flushes Rx buffer */
+#define	C_CM_FLUSH_TX	0x57		/* flushes Tx buffer */
+
+#define	C_CM_TXBEMPTY	0x60		/* Tx buffer is empty */
+#define	C_CM_TXLOWWM	0x61		/* Tx buffer low water mark */
+#define	C_CM_RXHIWM	0x62		/* Rx buffer high water mark */
+#define	C_CM_RXNNDT	0x63		/* rx no new data timeout */
+#define	C_CM_MDCD	0x70		/* modem DCD change */
+#define	C_CM_MDSR	0x71		/* modem DSR change */
+#define	C_CM_MRI	0x72		/* modem RI change */
+#define	C_CM_MCTS	0x73		/* modem CTS change */
+#define	C_CM_RXBRK	0x84		/* Break received */
+#define	C_CM_PR_ERROR	0x85		/* Parity error */
+#define	C_CM_FR_ERROR	0x86		/* Frame error */
+#define C_CM_OVR_ERROR  0x87            /* Overrun error */
+#define C_CM_RXOFL	0x88            /* RX buffer overflow */
+#define	C_CM_CMDERROR	0x90		/* command error */
+#define	C_CM_FATAL	0x91		/* fatal error */
+#define	C_CM_HW_RESET	0x92		/* reset board */
+
+/*
+ *	CH_CTRL - This per port structure contains all parameters
+ *	that control an specific port. It can be seen as the
+ *	configuration registers of a "super-serial-controller".
+ */
+
+struct CH_CTRL {
+	uclong	op_mode;	/* operation mode */
+	uclong	intr_enable;	/* interrupt masking */
+	uclong	sw_flow;	/* SW flow control */
+	uclong	flow_status;	/* output flow status */
+	uclong	comm_baud;	/* baud rate  - numerically specified */
+	uclong	comm_parity;	/* parity */
+	uclong	comm_data_l;	/* data length/stop */
+	uclong	comm_flags;	/* other flags */
+	uclong	hw_flow;	/* HW flow control */
+	uclong	rs_control;	/* RS-232 outputs */
+	uclong	rs_status;	/* RS-232 inputs */
+	uclong	flow_xon;	/* xon char */
+	uclong	flow_xoff;	/* xoff char */
+	uclong	hw_overflow;	/* hw overflow counter */
+	uclong	sw_overflow;	/* sw overflow counter */
+	uclong	comm_error;	/* frame/parity error counter */
+};
+
+
+/*
+ *	BUF_CTRL - This per channel structure contains
+ *	all Tx and Rx buffer control for a given channel.
+ */
+
+struct	BUF_CTRL	{
+	uclong	flag_dma;	/* buffers are in Host memory */
+	uclong	tx_bufaddr;	/* address of the tx buffer */
+	uclong	tx_bufsize;	/* tx buffer size */
+	uclong	tx_threshold;	/* tx low water mark */
+	uclong	tx_get;		/* tail index tx buf */
+	uclong	tx_put;		/* head index tx buf */
+	uclong	rx_bufaddr;	/* address of the rx buffer */
+	uclong	rx_bufsize;	/* rx buffer size */
+	uclong	rx_threshold;	/* rx high water mark */
+	uclong	rx_get;		/* tail index rx buf */
+	uclong	rx_put;		/* head index rx buf */
+	uclong	filler[5];	/* filler to align structures */
+};
+
+/*
+ *	BOARD_CTRL - This per board structure contains all global 
+ *	control fields related to the board.
+ */
+
+struct BOARD_CTRL {
+
+	/* static info provided by the on-board CPU */
+	uclong	n_channel;	/* number of channels */
+	uclong	fw_version;	/* firmware version */
+
+	/* static info provided by the driver */
+	uclong	op_system;	/* op_system id */
+	uclong	dr_version;	/* driver version */
+
+	/* board control area */
+	uclong	inactivity;	/* inactivity control */
+
+	/* host to FW commands */
+	uclong	hcmd_channel;	/* channel number */
+	uclong	hcmd_param;	/* pointer to parameters */
+
+	/* FW to Host commands */
+	uclong	fwcmd_channel;	/* channel number */
+	uclong	fwcmd_param;	/* pointer to parameters */
+
+	/* filler so the structures are aligned */
+	uclong	filler[7];
+};
+
+/*
+ *	ZFW_CTRL - This is the data structure that includes all other
+ *	data structures used by the Firmware.
+ */
+ 
+struct ZFW_CTRL {
+	struct BOARD_CTRL	board_ctrl;
+	struct CH_CTRL		ch_ctrl[MAX_CHAN];
+	struct BUF_CTRL		buf_ctrl[MAX_CHAN];
+};
+
+/****************** ****************** *******************/
+#endif
+
+
+
 
 #ifdef __KERNEL__
 
+/***************************************
+ * Memory access functions/macros      *
+ * (required to support Alpha systems) *
+ ***************************************/
+
+#define cy_writeb(port,val)     {writeb((ucchar)(val),(ulong)(port)); mb();}
+#define cy_writew(port,val)     {writew((ushort)(val),(ulong)(port)); mb();}
+#define cy_writel(port,val)     {writel((uclong)(val),(ulong)(port)); mb();}
+
+#define cy_readb(port)  readb(port)
+#define cy_readw(port)  readw(port)
+#define cy_readl(port)  readl(port)
+
 /* Per card data structure */
 
 struct cyclades_card {
-    int base_addr;
+    long base_addr;
+    long ctl_addr;
     int irq;
-    int num_chips;	/* 0 if card is absent */
+    int num_chips;	/* 0 if card absent, -1 if Z/PCI, else Y */
     int first_line;	/* minor number of first channel on card */
     int bus_index;	/* address shift - 0 for ISA, 1 for PCI */
+    int	inact_ctrl;	/* FW Inactivity control - 0 disabled, 1 enabled */
 };
 
 struct cyclades_chip {
@@ -62,23 +513,28 @@
 
 struct cyclades_port {
 	int                     magic;
-	int                     type;
 	int			card;
 	int			line;
 	int			flags; 		/* defined in tty.h */
+	int                     type;		/* UART type */
 	struct tty_struct 	*tty;
 	int			read_status_mask;
+	int			ignore_status_mask;
 	int			timeout;
 	int			xmit_fifo_size;
 	int                     cor1,cor2,cor3,cor4,cor5;
 	int                     tbpr,tco,rbpr,rco;
-	int			ignore_status_mask;
+	int			baud;
+	int			rflow;
+	int			rtsdtr_inv;
+	int			chip_rev;
+	int			custom_divisor;
+	int                     x_char; /* to be pushed out ASAP */
 	int			close_delay;
-	int			IER; 	/* Interrupt Enable Register */
-	int			event;
+	unsigned short		closing_wait;
+	unsigned long		event;
 	unsigned long		last_active;
 	int			count;	/* # of fd on device */
-	int                     x_char; /* to be pushed out ASAP */
 	int                     x_break;
 	int			blocked_open; /* # of blocked opens */
 	long			session; /* Session of opening process */
@@ -94,29 +550,50 @@
 	struct termios		callout_termios;
 	struct wait_queue	*open_wait;
 	struct wait_queue	*close_wait;
+	struct wait_queue	*shutdown_wait;
         struct cyclades_monitor mon;
+	unsigned long		jiffies[3];
+	unsigned long		rflush_count;
+	struct cyclades_idle_stats   idle_stats;
 };
 
 /*
  * Events are used to schedule things to happen at timer-interrupt
  * time, instead of at cy interrupt time.
  */
-#define Cy_EVENT_READ_PROCESS	0
-#define Cy_EVENT_WRITE_WAKEUP	1
-#define Cy_EVENT_HANGUP		2
-#define Cy_EVENT_BREAK		3
-#define Cy_EVENT_OPEN_WAKEUP	4
-
-
-
-#define CyMaxChipsPerCard 8
+#define Cy_EVENT_READ_PROCESS		0
+#define Cy_EVENT_WRITE_WAKEUP		1
+#define Cy_EVENT_HANGUP			2
+#define Cy_EVENT_BREAK			3
+#define Cy_EVENT_OPEN_WAKEUP		4
+#define Cy_EVENT_SHUTDOWN_WAKEUP	5
+
+#define	CLOSING_WAIT_DELAY	60*HZ
+#define CY_CLOSING_WAIT_NONE	65535
+#define CY_CLOSING_WAIT_INF	0
+
+
+#define CyMAX_CHIPS_PER_CARD	8
+#define CyMAX_CHAR_FIFO		12
+#define CyPORTS_PER_CHIP	4
+#define	CD1400_MAX_SPEED	115200
+
+#define	CyISA_Ywin	0x2000
+
+#define CyPCI_Ywin 	0x4000
+#define CyPCI_Zctl 	CTRL_WINDOW_SIZE
+#define CyPCI_Zwin 	0x80000
+#define CyPCI_Ze_win 	(2 * CyPCI_Zwin)
 
 /**** CD1400 registers ****/
 
-#define CyRegSize  0x0400
-#define Cy_HwReset 0x1400
-#define Cy_ClrIntr 0x1800
-#define Cy_EpldRev 0x1e00
+#define CD1400_REV_G	0x46
+#define CD1400_REV_J	0x48
+
+#define CyRegSize  	0x0400
+#define Cy_HwReset 	0x1400
+#define Cy_ClrIntr 	0x1800
+#define Cy_EpldRev 	0x1e00
 
 /* Global Registers */
 
@@ -152,6 +629,9 @@
 #define CyPPR 		(0x7E*2)
 #define      CyCLOCK_20_1MS	(0x27)
 #define      CyCLOCK_25_1MS	(0x31)
+#define      CyCLOCK_25_5MS	(0xf4)
+#define      CyCLOCK_60_1MS	(0x75)
+#define      CyCLOCK_60_2MS	(0xea)
 
 /* Virtual Registers */
 
@@ -274,9 +754,12 @@
 #define CyTBPR		(0x72*2)
 #define CyTCOR		(0x76*2)
 
-/* max number of chars in the FIFO */
+/* Custom Registers */
 
-#define CyMAX_CHAR_FIFO	12
+#define	CyPLX_VER	(0x3400)
+#define	PLX_9050	0x0b
+#define	PLX_9060	0x0c
+#define	PLX_9080	0x0d
 
 /***************************************************************************/
 
diff -u --recursive --new-file v2.0.35/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.0.35/linux/include/linux/fs.h	Sun Nov 15 10:49:52 1998
+++ linux/include/linux/fs.h	Sun Nov 15 10:33:17 1998
@@ -75,6 +75,7 @@
 #define S_IMMUTABLE	512	/* Immutable file */
 #define MS_NOATIME	1024	/* Do not update access times. */
 #define S_BAD_INODE	2048	/* Marker for unreadable inodes */
+#define S_ZERO_WR	4096	/* Device accepts 0 length writes */
 
 /*
  * Flags that can be altered by MS_REMOUNT
@@ -106,6 +107,7 @@
 #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        IS_ZERO_WR(inode) ((inode)->i_flags & S_ZERO_WR)
 
 #define UPDATE_ATIME(inode) \
 	if (!IS_NOATIME(inode) && !IS_RDONLY(inode)) { \
@@ -552,6 +554,7 @@
 
 asmlinkage int sys_open(const char *, int, int);
 asmlinkage int sys_close(unsigned int);		/* yes, it's really unsigned */
+asmlinkage int sys_read(unsigned int, char *, int);
 
 extern void kill_fasync(struct fasync_struct *fa, int sig);
 
diff -u --recursive --new-file v2.0.35/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v2.0.35/linux/include/linux/genhd.h	Mon Jul 13 13:46:41 1998
+++ linux/include/linux/genhd.h	Sun Nov 15 10:33:17 1998
@@ -43,7 +43,8 @@
 	unsigned char end_cyl;		/* end cylinder */
 	unsigned int start_sect;	/* starting sector counting from 0 */
 	unsigned int nr_sects;		/* nr of sectors in partition */
-};
+} __attribute((packed));	/* Give a polite hint to egcs/alpha to generate
+				   unaligned operations */
 
 struct hd_struct {
 	long start_sect;
diff -u --recursive --new-file v2.0.35/linux/include/linux/if_shaper.h linux/include/linux/if_shaper.h
--- v2.0.35/linux/include/linux/if_shaper.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/if_shaper.h	Sun Nov 15 10:33:17 1998
@@ -0,0 +1,62 @@
+#ifndef __LINUX_SHAPER_H
+#define __LINUX_SHAPER_H
+
+#ifdef __KERNEL__
+
+#define SHAPER_QLEN	10
+/*
+ *	This is a bit speed dependant (read it shouldnt be a constant!)
+ *
+ *	5 is about right for 28.8 upwards. Below that double for every
+ *	halving of speed or so. - ie about 20 for 9600 baud.
+ */
+#define SHAPER_LATENCY	(5*HZ)
+#define SHAPER_MAXSLIP	2
+#define SHAPER_BURST	(HZ/50)		/* Good for >128K then */
+
+struct shaper
+{
+	struct sk_buff_head sendq;
+	__u32 bitspersec;
+	__u32 bytespertick;
+	__u32 shapelatency;
+	__u32 shapeclock;
+	__u32 recovery;		/* Time we can next clock a packet out on
+				   an empty queue */
+	char locked;
+	struct device *dev;
+	int  (*hard_start_xmit) (struct sk_buff *skb,
+		struct device *dev);
+	int  (*hard_header) (struct sk_buff *skb,
+		struct device *dev,
+		unsigned short type,
+		void *daddr,
+		void *saddr,
+		unsigned len);
+	int  (*rebuild_header)(void *eth, struct device *dev,
+                unsigned long raddr, struct sk_buff *skb);
+	struct enet_statistics* (*get_stats)(struct device *dev);
+	struct wait_queue *wait_queue;
+	struct timer_list timer;
+};
+
+#endif
+
+#define SHAPER_SET_DEV		0x0001
+#define SHAPER_SET_SPEED	0x0002
+#define SHAPER_GET_DEV		0x0003
+#define SHAPER_GET_SPEED	0x0004
+
+struct shaperconf
+{
+	__u16	ss_cmd;
+	union
+	{
+		char 	ssu_name[14];
+		__u32	ssu_speed;
+	} ss_u;
+#define ss_speed ss_u.ssu_speed
+#define ss_name ss_u.ssu_name
+};
+
+#endif
diff -u --recursive --new-file v2.0.35/linux/include/linux/if_wic.h linux/include/linux/if_wic.h
--- v2.0.35/linux/include/linux/if_wic.h	Wed Oct 15 15:11:27 1997
+++ linux/include/linux/if_wic.h	Wed Dec 31 16:00:00 1969
@@ -1,102 +0,0 @@
-#ifndef _LINUX_IF_WIC_H
-#define _LINUX_IF_WIC_H
-
-#include <linux/sockios.h>
-
-#define	SIOCDEVWIC	SIOCDEVPRIVATE
-
-struct wicconf
-{
-	unsigned char 	pcmd;
-	unsigned char	data[120];
-	unsigned char	len;
-};
-
-/* WIC host to controller commands */
-
-#define WIC_AYT		0x10	/* test dki */
-#define WIC_RESET	0x11	/* reset controller */
-#define WIC_SETSN	0x21	/* set station name */
-#define WIC_SETPS	0x22	/* set power saving mode */
-#define WIC_SETAF	0x23	/* set announce filter */
-#define WIC_SETGPF	0x24	/* set GPSP filter */
-#define WIC_GETVERH	0x61	/* get interface controller version */
-#define WIC_GETNL	0x62	/* get neighbor list */
-#define WIC_GETSN	0x65	/* get station name */
-#define WIC_CLRSTATS	0x83	/* clear controller statistics */
-#define WIC_SETNET	0x84	/* set network configuration */
-#define WIC_SETSYS	0x85	/* set system configuration */
-#define WIC_GETSTATS	0xc1	/* get statistics */
-#define WIC_GETVERM	0xc3	/* get MAC version */
-#define WIC_GETNET	0xc4	/* get network configuration */
-#define WIC_GETSYS	0xc5	/* get system configuration */
-
-/*
- * structure used for the GETNET/SETNET command
- */
-
-struct wic_net {
-	unsigned char ula[6];		/* ula of interface */
-	unsigned char mode;		/* operating mode */
-#define NET_MODE_ME		0x01	/* receive my ula */
-#define NET_MODE_BCAST		0x02	/* receive bcasts */
-#define NET_MODE_MCAST		0x04	/* receive mcasts */
-#define NET_MODE_PROM		0x08	/* promiscuous */
-#define NET_MODE_HC		0x10	/* is a hop coordinator */
-#define NET_MODE_HC_VALID	0x20	/* hc address is valid */
-#define NET_MODE_HCAP		0x40	/* hc is also ap */
-#define NET_MODE_HC_KNOWN	0x80	/* hc is known */
-	unsigned char rts_lo;		/* rts threshold */
-	unsigned char rts_hi;		/* rts threshold */
-	unsigned char retry;		/* retry limit */
-	unsigned char hc_ula[6];	/* ula of hc */
-	unsigned char key[4];		/* network key */
-	unsigned char dsl;		/* direct send limit */
-	unsigned char res1;		/* reserved */
-};
-
-/*
- * structure used for the GETSYS/SETSYS command 
- */
-
-struct wic_sys {
-	unsigned char mode;		/* set operating mode */
-#define SYS_MODE_ANT_DIV	0x00	/* use antenna diversity */
-#define SYS_MODE_ANT_1		0x01	/* use ant 1 for tx */
-#define SYS_MODE_ANT_2		0x02	/* use ant 2 for tx */
-#define SYS_MODE_HC_LOCK	0x04	/* lock onto current hc */
-#define SYS_MODE_DEBUG		0x08	/* upload failed frames */
-#define SYS_MODE_IAM_AP		0x10	/* I am AP */
-#define SYS_MODE_IAM_HC		0x20	/* I am HC */
-#define SYS_MODE_USE_SKIP	0x40	/* use skipping mechanism */
-#define SYS_MODE_AUTO		0x80	/* station is in auto mode */
-	unsigned char switches;		/* radio/controller switches */
-#define SYS_SWITCH_STDBY	0x01	/* switch radio to standby */
-#define SYS_SWITCH_TXRX		0x02	/* 1 = tx, manual mode only */
-#define SYS_SWITCH_PA		0x04	/* 1 = enable PA on radio */
-#define SYS_SWITCH_PWR		0x10	/* 1 = hi, 0 = lo power output */
-#define SYS_SWITCH_RES1		0x20	/* reserved, must be 0 */
-#define SYS_SWITCH_LIGHTS	0x40	/* light for tx & rx */
-#define SYS_SWITCH_LIGHTS_HC	0x80	/* light for rx while coordinated */
-	unsigned char hop_min;		/* hop range */
-	unsigned char hop_max;		/* hop range */
-	unsigned char pre_len;		/* preamble length (bytes) */
-	unsigned char pre_match;	/* valid preamble match (bytes) */
-	unsigned char mod;		/* data mod: 1 = 8:1, 0 = none */
-	unsigned char cca_mode;		/* cca flags */
-#define CCA_PKT_DET_BSY		0x01	/* busy if packet is detected */
-#define CCA_VIRT_CARR		0x02	/* use virtual carrier */
-#define CCA_RSSI_BSY		0x04	/* busy if rssi > threshold */
-#define CCA_DATA_BSY		0x08	/* busy if valid data > XXX usec */
-	unsigned char dwell_hi;		/* dwell time */
-	unsigned char dwell_lo;		/* dwell time */
-	unsigned char hc_timeout;	/* HC timeout */
-	unsigned char rssi;		/* rssi threshold */
-	unsigned char hc_rssi;		/* rssi of last hc frame */
-	unsigned char hc_rssi_chan;	/* channel of hc rssi value */
-};
-
-
-#endif /* _LINUX_IF_WIC_H */
-
-
diff -u --recursive --new-file v2.0.35/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.0.35/linux/include/linux/interrupt.h	Thu Aug 14 13:30:58 1997
+++ linux/include/linux/interrupt.h	Sun Nov 15 10:33:17 1998
@@ -39,7 +39,8 @@
 	IMMEDIATE_BH,
 	KEYBOARD_BH,
 	CYCLADES_BH,
-	CM206_BH
+	CM206_BH,
+	ISICOM_BH
 };
 
 extern inline void init_bh(int nr, void (*routine)(void))
diff -u --recursive --new-file v2.0.35/linux/include/linux/ip.h linux/include/linux/ip.h
--- v2.0.35/linux/include/linux/ip.h	Wed Oct 15 15:23:10 1997
+++ linux/include/linux/ip.h	Sun Nov 15 10:33:17 1998
@@ -65,7 +65,7 @@
 
 #define	IPOPT_TS_TSONLY		0		/* timestamps only */
 #define	IPOPT_TS_TSANDADDR	1		/* timestamps and addresses */
-#define	IPOPT_TS_PRESPEC	2		/* specified modules only */
+#define	IPOPT_TS_PRESPEC	3		/* specified modules only */
 
 struct options {
   __u32		faddr;				/* Saved first hop address */
diff -u --recursive --new-file v2.0.35/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v2.0.35/linux/include/linux/isdn.h	Wed Oct 15 15:25:22 1997
+++ linux/include/linux/isdn.h	Sun Nov 15 10:33:17 1998
@@ -1,8 +1,8 @@
-/* $Id: isdn.h,v 1.32 1997/08/21 09:49:46 fritz Exp $
+/* $Id: isdn.h,v 1.31.2.17 1998/11/05 22:13:28 fritz Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
  * 
@@ -21,8 +21,72 @@
  * 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.2.17  1998/11/05 22:13:28  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.31.2.16  1998/11/04 17:22:53  fritz
+ * Replaced broken lowlevel-driver locking.
+ *
+ * Revision 1.31.2.15  1998/11/03 14:31:57  fritz
+ * Reduced stack usage in various functions.
+ * Adapted statemachine to work with certified HiSax.
+ * Some fixes in callback handling.
+ *
+ * Revision 1.31.2.14  1998/10/25 14:37:37  fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.31.2.13  1998/10/23 10:14:25  paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.31.2.12  1998/08/22 16:41:25  armin
+ * Added silence detection in audio receive mode (AT+VSD).
+ *
+ * Revision 1.31.2.11  1998/07/15 15:04:19  calle
+ * make isdn4k-utils compile again.
+ *
+ * Revision 1.31.2.10  1998/06/07 13:48:30  fritz
+ * ABC cleanup
+ *
+ * Revision 1.31.2.9  1998/06/02 12:12:49  detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.31.2.8  1998/05/06 08:30:44  detabc
+ * add Item to stop icmp-unreach (max. 6 times of dialwait delay)
+ *
+ * Revision 1.31.2.7  1998/04/26 19:51:40  detabc
+ * removed unused code
+ *
+ * Revision 1.31.2.6  1998/04/26 11:10:46  detabc
+ * add items for the abc_tx_queus and the abc_delayed_hangup
+ * only used if the abc-extension is enabled
+ *
+ * Revision 1.31.2.5  1998/04/18 17:39:45  detabc
+ * remove some unused abc-lines
+ * added defines und items for abc-secure callback (only used with abc-extenrsion)
+ *
+ * Revision 1.31.2.4  1998/04/08 21:42:25  keil
+ * Blocksize default 1024
+ *
+ * Revision 1.31.2.3  1998/03/16 09:56:28  cal
+ * Merged in TimRu-patches. Still needs validation in conjunction with ABC-patches.
+ *
+ * Revision 1.31.2.2  1998/03/07 23:35:45  detabc
+ * added the abc-extension to the linux isdn-kernel
+ * for kernel-version 2.0.xx
+ * DO NOT USE FOR HIGHER KERNELS-VERSIONS
+ * all source-lines are switched with the define  CONFIG_ISDN_WITH_ABC
+ * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes
+ *
+ * you need also a modified isdnctrl-source the switch on the
+ * features of the abc-extension
+ *
+ * please use carefully. more detail will be follow.
+ * thanks
+ *
+ * Revision 1.31.2.1  1997/08/21 15:57:04  fritz
+ * Synchronized 2.0.X branch with 2.0.31-pre7
  *
  * Revision 1.31  1997/06/22 11:57:07  fritz
  * Added ability to adjust slave triggerlevel.
@@ -137,6 +201,7 @@
 #ifndef isdn_h
 #define isdn_h
 
+#include <linux/config.h>
 #include <linux/ioctl.h>
 
 #define ISDN_TTY_MAJOR    43
@@ -148,14 +213,20 @@
  * the correspondent code in isdn.c
  */
 
+#ifdef CONFIG_COBALT_MICRO_SERVER
+/* Save memory */
+#define ISDN_MAX_DRIVERS    2
+#define ISDN_MAX_CHANNELS   8
+#else
 #define ISDN_MAX_DRIVERS    32
 #define ISDN_MAX_CHANNELS   64
+#endif
 #define ISDN_MINOR_B        0
 #define ISDN_MINOR_BMAX     (ISDN_MAX_CHANNELS-1)
-#define ISDN_MINOR_CTRL     ISDN_MAX_CHANNELS
-#define ISDN_MINOR_CTRLMAX  (2*ISDN_MAX_CHANNELS-1)
-#define ISDN_MINOR_PPP      (2*ISDN_MAX_CHANNELS)
-#define ISDN_MINOR_PPPMAX   (3*ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_CTRL     64
+#define ISDN_MINOR_CTRLMAX  (64 + (ISDN_MAX_CHANNELS-1))
+#define ISDN_MINOR_PPP      128
+#define ISDN_MINOR_PPPMAX   (128 + (ISDN_MAX_CHANNELS-1))
 #define ISDN_MINOR_STATUS   255
 
 /* New ioctl-codes */
@@ -166,8 +237,8 @@
 #define IIOCNETANM  _IO('I',5)
 #define IIOCNETDNM  _IO('I',6)
 #define IIOCNETGNM  _IO('I',7)
-#define IIOCGETSET  _IO('I',8)
-#define IIOCSETSET  _IO('I',9)
+/* #define IIOCGETSET  _IO('I',8) obsolete */
+/* #define IIOCSETSET  _IO('I',9) obsolete */
 #define IIOCSETVER  _IO('I',10)
 #define IIOCNETHUP  _IO('I',11)
 #define IIOCSETGST  _IO('I',12)
@@ -210,6 +281,7 @@
 
 #define ISDN_MODEM_ANZREG    23        /* Number of Modem-Registers        */
 #define ISDN_MSNLEN          20
+#define ISDN_LMSNLEN         255 /* Length of tty's Listen-MSN string */
 
 typedef struct {
   char drvid[25];
@@ -230,6 +302,7 @@
 
 #define NET_DV 0x04 /* Data version for net_cfg     */
 #define TTY_DV 0x04 /* Data version for iprofd etc. */
+#define INF_DV 0x01 /* Data version for /dev/isdninfo */
 
 typedef struct {
   char name[10];     /* Name of interface                     */
@@ -254,8 +327,17 @@
   int  pppbind;      /* ippp device for bindings              */
   int  chargeint;    /* Use fixed charge interval length      */
   int  triggercps;   /* BogoCPS needed for triggering slave   */
+  int  dialtimeout;  /* Dial-Timeout                          */
+  int  dialwait;     /* Time to wait after failed dial        */
+  int  dialmode;     /* Flag: off / on / auto                 */
 } isdn_net_ioctl_cfg;
 
+#define ISDN_NET_DIALMODE_MASK 0xC0  /* bits for status                   */
+#define  ISDN_NET_DM_OFF	0x00    /* this interface is stopped      */
+#define  ISDN_NET_DM_MANUAL	0x40    /* this interface is on (manual)  */
+#define  ISDN_NET_DM_AUTO	0x80    /* this interface is autodial     */
+#define ISDN_NET_DIALMODE(x) ((&(x))->flags & ISDN_NET_DIALMODE_MASK)
+
 #ifdef __KERNEL__
 
 #ifndef STANDALONE
@@ -346,6 +428,7 @@
 /* Timeout-Values for isdn_net_dial() */
 #define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
 #define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
+#define ISDN_TIMER_DTIMEOUT60 (60*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
 
 /* GLOBAL_FLAGS */
 #define ISDN_GLOBAL_STOPPED 1
@@ -364,6 +447,12 @@
 #define ISDN_NET_TMP        0x10       /* tmp interface until getting an IP */
 #define ISDN_NET_DYNAMIC    0x20       /* this link is dynamically allocated */
 #endif
+
+/*
+ * also see the ISDN_NET_DM_* defines earlier
+ * (they are not here, as we need access to those in userspace)
+ */
+
 #define ISDN_NET_MAGIC      0x49344C02 /* for paranoia-checking             */
 
 /* Phone-list-element */
@@ -372,6 +461,7 @@
   char num[ISDN_MSNLEN];
 } isdn_net_phone;
 
+
 /* Local interface-data */
 typedef struct isdn_net_local_s {
   ulong                  magic;
@@ -423,7 +513,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 */
+  int  					triggercps;	/* BogoCPS needed for triggering slave   */
   struct device          *srobin;      /* Ptr to Master device for slaves  */
   isdn_net_phone         *phone[2];    /* List of remote-phonenumbers      */
 				       /* phone[0] = Incoming Numbers      */
@@ -460,6 +550,11 @@
 				    struct device *,
                                     unsigned char *);
   int  pppbind;                        /* ippp device for bindings         */
+  int					dialtimeout;	/* How long shall we try on dialing? (jiffies) */
+  int			dialwait;		/* How long shall we wait after failed attempt? (jiffies) */
+  ulong			dialstarted;	/* jiffies of first dialing-attempt */
+  ulong			dialwait_timer;	/* jiffies of earliest next dialing-attempt */
+  int			huptimeout;	/* How long will the connection be up? (seconds) */
 } isdn_net_local;
 
 #ifdef CONFIG_ISDN_PPP
@@ -502,7 +597,8 @@
 #define ISDN_ASYNC_PGRP_LOCKOUT       0x0200 /* Lock cua opens on pgrp       */
 #define ISDN_ASYNC_CALLOUT_NOHUP      0x0400 /* No hangup for cui            */
 #define ISDN_ASYNC_SPLIT_TERMIOS      0x0008 /* Sep. termios for dialin/out  */
-#define ISDN_SERIAL_XMIT_SIZE           4000 /* Maximum bufsize for write    */
+#define ISDN_SERIAL_XMIT_SIZE           1024 /* default bufsize for write    */
+#define ISDN_SERIAL_XMIT_MAX            4000 /* Maximum bufsize for write    */
 #define ISDN_SERIAL_TYPE_NORMAL            1
 #define ISDN_SERIAL_TYPE_CALLOUT           2
 
@@ -581,6 +677,7 @@
   void                  *adpcms;         /* state for adpcm decompression  */
   void                  *adpcmr;         /* state for adpcm compression    */
   void                  *dtmf_state;     /* state for dtmf decoder         */
+  void			*silence_state;	 /* state for silence detection	   */
 #endif
   struct tty_struct 	*tty;            /* Pointer to corresponding tty   */
   atemu                 emu;             /* AT-emulator data               */
@@ -624,7 +721,7 @@
 struct mpqueue {
   struct mpqueue *next;
   struct mpqueue *last;
-  long sqno;
+  long    sqno;
   struct sk_buff *skb;
   int BEbyte;
   unsigned long time;
@@ -663,8 +760,12 @@
   struct slcompress *slcomp;
 #endif
   unsigned long debug;
-  struct isdn_ppp_compressor *compressor,*link_compressor;
+  struct isdn_ppp_compressor *compressor, *link_compressor;
   void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
+#ifdef ISDN_SYNCPPP_READDRESS
+  unsigned long	old_pa_addr;
+  unsigned long	old_pa_dstaddr;
+#endif
 };
 
 #endif
@@ -678,16 +779,19 @@
   char *private;
 } infostruct;
 
+#define DRV_FLAG_RUNNING 1
+#define DRV_FLAG_REJBUS  2
+#define DRV_FLAG_LOADED  4
+
 /* Description of hardware-level-driver */
 typedef struct {
-  ulong               flags;            /* Flags                            */
+  ulong               online;           /* Channel-Online flags             */
+  ulong               flags;            /* Misc driver flags                */
+  int                 locks;            /* Number of locks for this driver  */
   int                 channels;         /* Number of channels               */
-  int                 reject_bus;       /* Flag: Reject rejected call on bus*/
   struct wait_queue  *st_waitq;         /* Wait-Queue for status-read's     */
   int                 maxbufsize;       /* Maximum Buffersize supported     */
   unsigned long       pktcount;         /* Until now: unused                */
-  int                 running;          /* Flag: Protocolcode running       */
-  int                 loaded;           /* Flag: Driver loaded              */
   int                 stavail;          /* Chars avail on Status-device     */
   isdn_if            *interface;        /* Interface to driver              */
   int                *rcverr;           /* Error-counters for B-Ch.-receive */
diff -u --recursive --new-file v2.0.35/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- v2.0.35/linux/include/linux/isdn_ppp.h	Mon Aug  4 17:34:01 1997
+++ linux/include/linux/isdn_ppp.h	Sun Nov 15 10:33:17 1998
@@ -8,6 +8,8 @@
 #define CALLTYPE_OUTGOING 0x2
 #define CALLTYPE_CALLBACK 0x4
 
+#define IPPP_VERSION    "2.2.0"
+
 struct pppcallinfo
 {
 	int calltype;
diff -u --recursive --new-file v2.0.35/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v2.0.35/linux/include/linux/isdnif.h	Wed Oct 15 15:25:02 1997
+++ linux/include/linux/isdnif.h	Sun Nov 15 10:33:17 1998
@@ -1,10 +1,10 @@
-/* $Id: isdnif.h,v 1.20 1997/05/27 15:18:06 fritz Exp $
+/* $Id: isdnif.h,v 1.20.2.2 1998/11/05 22:13:33 fritz Exp $
  *
  * Linux ISDN subsystem
  *
  * Definition of the interface between the subsystem and its low-level drivers.
  *
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
  * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
  * 
  * This program is free software; you can redistribute it and/or modify
@@ -22,6 +22,12 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.20.2.2  1998/11/05 22:13:33  fritz
+ * Changed mail-address.
+ *
+ * Revision 1.20.2.1  1998/03/07 23:00:50  tsbogend
+ * added defines for Linux/Alpha 2.0.x with alpha-patches
+ *
  * Revision 1.20  1997/05/27 15:18:06  fritz
  * Added changes for recent 2.1.x kernels:
  *   changed return type of isdn_close
@@ -382,9 +388,15 @@
 }
 
 #define GET_USER(x, addr) ( x = get_user(addr) )
+#ifdef __alpha__ /* needed for 2.0.x with alpha-patches */
+#define RWTYPE long
+#define LSTYPE long
+#define RWARG unsigned long
+#else
 #define RWTYPE int
 #define LSTYPE int
 #define RWARG int
+#endif
 #define LSARG off_t
 #else
 #include <asm/uaccess.h>
diff -u --recursive --new-file v2.0.35/linux/include/linux/isicom.h linux/include/linux/isicom.h
--- v2.0.35/linux/include/linux/isicom.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/isicom.h	Sun Nov 15 10:33:17 1998
@@ -0,0 +1,300 @@
+#ifndef _LINUX_ISICOM_H
+#define _LINUX_ISICOM_H
+
+/*#define		ISICOM_DEBUG*/
+/*#define		ISICOM_DEBUG_DTR_RTS*/
+
+
+/*
+ *	Firmware Loader definitions ...
+ */
+ 
+#define		__MultiTech		('M'<<8)
+#define		MIOCTL_LOAD_FIRMWARE	(__MultiTech | 0x01)
+#define         MIOCTL_READ_FIRMWARE    (__MultiTech | 0x02)
+#define         MIOCTL_XFER_CTRL	(__MultiTech | 0x03)
+#define         MIOCTL_RESET_CARD	(__MultiTech | 0x04)
+
+#define		DATA_SIZE	16
+
+typedef	struct	{
+		unsigned short	exec_segment;
+		unsigned short	exec_addr;
+}	exec_record;
+
+typedef	struct	{
+		int		board;		/* Board to load */
+		unsigned short	addr;
+		unsigned short	count;
+}	bin_header;
+
+typedef	struct	{
+		int		board;		/* Board to load */
+		unsigned short	addr;
+		unsigned short	count;
+		unsigned short	segment;
+		unsigned char	bin_data[DATA_SIZE];
+}	bin_frame;
+
+#ifdef __KERNEL__
+
+#define		YES	1
+#define		NO	0
+
+#define		ISILOAD_MISC_MINOR	155	/* /dev/isctl */
+#define		ISILOAD_NAME		"ISILoad"
+
+/*	
+ *  ISICOM Driver definitions ...
+ *
+ */
+
+#define		ISICOM_NAME	"ISICom"
+
+/*
+ *	These are now officially allocated numbers
+ */
+
+#define		ISICOM_NMAJOR	112	/* normal  */
+#define		ISICOM_CMAJOR	113	/* callout */
+#define		ISICOM_MAGIC	(('M' << 8) | 'T')
+
+#define		WAKEUP_CHARS	256	/* hard coded for now	*/ 
+#define		TX_SIZE		254 
+ 
+#define		BOARD_COUNT	4
+#define		PORT_COUNT	(BOARD_COUNT*16)
+
+#define		SERIAL_TYPE_NORMAL	1
+#define		SERIAL_TYPE_CALLOUT	2
+
+/*   character sizes  */
+
+#define		ISICOM_CS5		0x0000
+#define		ISICOM_CS6		0x0001
+#define		ISICOM_CS7		0x0002
+#define		ISICOM_CS8		0x0003
+
+/* stop bits */
+
+#define		ISICOM_1SB		0x0000
+#define		ISICOM_2SB		0x0004
+
+/* parity */
+
+#define		ISICOM_NOPAR		0x0000
+#define		ISICOM_ODPAR		0x0008
+#define		ISICOM_EVPAR		0x0018
+
+/* flow control */
+
+#define		ISICOM_CTSRTS		0x03
+#define		ISICOM_INITIATE_XONXOFF	0x04
+#define		ISICOM_RESPOND_XONXOFF	0x08
+
+#define InterruptTheCard(base) (outw(0,(base)+0xc)) 
+#define ClearInterrupt(base) (inw((base)+0x0a))	
+
+#define	BOARD(line)  (((line) >> 4) & 0x3)
+#define MIN(a, b) ( (a) < (b) ? (a) : (b) )
+
+	/*	isi kill queue bitmap	*/
+	
+#define		ISICOM_KILLTX		0x01
+#define		ISICOM_KILLRX		0x02
+
+	/* isi_board status bitmap */
+	
+#define		FIRMWARE_LOADED		0x0001
+#define		BOARD_ACTIVE		0x0002
+
+ 	/* isi_port status bitmap  */
+
+#define		ISI_CTS			0x1000
+#define		ISI_DSR			0x2000
+#define		ISI_RI			0x4000
+#define		ISI_DCD			0x8000
+#define		ISI_DTR			0x0100
+#define		ISI_RTS			0x0200
+
+
+#define		ISI_TXOK		0x0001 
+ 
+struct	isi_board {
+	unsigned short		base;
+	unsigned char		irq;
+	unsigned char		port_count;
+	unsigned short		status;
+	unsigned short		port_status; /* each bit represents a single port */
+	unsigned short		shift_count;
+	struct isi_port		* ports;
+	signed char		count;
+};
+
+struct	isi_port {
+	unsigned short		magic;
+	unsigned int		flags;
+	int			count;
+	int			blocked_open;
+	int			close_delay;
+	unsigned short		channel;
+	unsigned short		status;
+	unsigned short		closing_wait;
+	long 			session;
+	long			pgrp;
+	struct isi_board	* card;
+	struct tty_struct 	* tty;
+	struct wait_queue	* close_wait;
+	struct wait_queue	* open_wait;
+	struct tq_struct	hangup_tq;
+	struct tq_struct	bh_tqueue;
+	unsigned char		* xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct termios 		normal_termios;
+	struct termios		callout_termios;
+};
+
+
+/*
+ *  ISI Card specific ops ...
+ */
+ 
+extern inline void raise_dtr(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+	short wait=300;
+	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+	if (wait <= 0) {
+		printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG_DTR_RTS	
+	printk(KERN_DEBUG "ISICOM: raise_dtr.\n");
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+	outw(0x0504, base);
+	InterruptTheCard(base);
+	port->status |= ISI_DTR;
+}
+extern inline void drop_dtr(struct isi_port * port)
+{	
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+	short wait=300;
+	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+	if (wait <= 0) {
+		printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG_DTR_RTS	
+	printk(KERN_DEBUG "ISICOM: drop_dtr.\n");
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+	outw(0x0404, base);
+	InterruptTheCard(base);	
+	port->status &= ~ISI_DTR;
+}
+extern inline void raise_rts(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+	short wait=300;
+	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+	if (wait <= 0) {
+		printk(KERN_WARNING "ISICOM: Card found busy in raise_rts.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG_DTR_RTS	
+	printk(KERN_DEBUG "ISICOM: raise_rts.\n");
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+	outw(0x0a04, base);
+	InterruptTheCard(base);	
+	port->status |= ISI_RTS;
+}
+extern inline void drop_rts(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+	short wait=300;
+	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+	if (wait <= 0) {
+		printk(KERN_WARNING "ISICOM: Card found busy in drop_rts.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG_DTR_RTS	
+	printk(KERN_DEBUG "ISICOM: drop_rts.\n");
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+	outw(0x0804, base);
+	InterruptTheCard(base);	
+	port->status &= ~ISI_RTS;
+}
+extern inline void raise_dtr_rts(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+	short wait=300;
+	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+	if (wait <= 0) {
+		printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr_rts.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG_DTR_RTS	
+	printk(KERN_DEBUG "ISICOM: raise_dtr_rts.\n");
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+	outw(0x0f04, base);
+	InterruptTheCard(base);
+	port->status |= (ISI_DTR | ISI_RTS);
+}
+extern inline void drop_dtr_rts(struct isi_port * port)
+{
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+	short wait=300;
+	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+	if (wait <= 0) {
+		printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr_rts.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG_DTR_RTS	
+	printk(KERN_DEBUG "ISICOM: drop_dtr_rts.\n");
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+	outw(0x0c04, base);
+	InterruptTheCard(base);	
+	port->status &= ~(ISI_RTS | ISI_DTR);
+}
+
+extern inline void kill_queue(struct isi_port * port, short queue)
+{
+	struct isi_board * card = port->card;
+	unsigned short base = card->base;
+	unsigned char channel = port->channel;
+	short wait=300;
+	while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+	if (wait <= 0) {
+		printk(KERN_WARNING "ISICOM: Card found busy in kill_queue.\n");
+		return;
+	}
+#ifdef ISICOM_DEBUG	
+	printk(KERN_DEBUG "ISICOM: kill_queue 0x%x.\n", queue);
+#endif	
+	outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+	outw((queue << 8) | 0x06, base);
+	InterruptTheCard(base);	
+}
+
+#endif	/*	__KERNEL__	*/
+
+#endif	/*	ISICOM_H	*/
\ No newline at end of file
diff -u --recursive --new-file v2.0.35/linux/include/linux/kerneld.h linux/include/linux/kerneld.h
--- v2.0.35/linux/include/linux/kerneld.h	Sat May 11 00:35:04 1996
+++ linux/include/linux/kerneld.h	Sun Nov 15 10:33:17 1998
@@ -1,6 +1,8 @@
 #ifndef _LINUX_KERNELD_H
 #define _LINUX_KERNELD_H
 
+#include <linux/posix_types.h>
+
 #define KERNELD_SYSTEM 1
 #define KERNELD_REQUEST_MODULE 2 /* "insmod" */
 #define KERNELD_RELEASE_MODULE 3 /* "rmmod" */
diff -u --recursive --new-file v2.0.35/linux/include/linux/lp.h linux/include/linux/lp.h
--- v2.0.35/linux/include/linux/lp.h	Thu Nov 30 04:03:38 1995
+++ linux/include/linux/lp.h	Sun Nov 15 10:33:17 1998
@@ -20,6 +20,7 @@
 #define LP_ABORT 0x0040
 #define LP_CAREFUL 0x0080
 #define LP_ABORTOPEN 0x0100
+#define LP_STRICT 0x0200
 
 /* timeout for each character.  This is relative to bus cycles -- it
  * is the count in a busy loop.  THIS IS THE VALUE TO CHANGE if you
@@ -70,6 +71,7 @@
 #define LPRESET     0x060c  /* reset printer */
 #define LPGETSTATS  0x060d  /* get statistics (struct lp_stats) */
 #define LPGETFLAGS  0x060e  /* get status flags */
+#define LPSTRICT    0x060f  /* enable/disable strict compliance */
 
 /* timeout for printk'ing a timeout, in jiffies (100ths of a second).
    This is also used for re-checking error conditions if LP_ABORT is
diff -u --recursive --new-file v2.0.35/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v2.0.35/linux/include/linux/msdos_fs.h	Mon Jul 13 13:46:42 1998
+++ linux/include/linux/msdos_fs.h	Sun Nov 15 10:33:17 1998
@@ -7,7 +7,6 @@
 #include <linux/fs.h>
 #include <linux/stat.h>
 #include <linux/fd.h>
-#include <linux/config.h>
 #include <asm/byteorder.h>
 
 #define MSDOS_ROOT_INO  1 /* == MINIX_ROOT_INO */
diff -u --recursive --new-file v2.0.35/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.0.35/linux/include/linux/pci.h	Mon Jul 13 13:46:42 1998
+++ linux/include/linux/pci.h	Sun Nov 15 10:33:18 1998
@@ -546,8 +546,11 @@
 #define PCI_DEVICE_ID_DATABOOK_87144	0xb106
 
 #define PCI_VENDOR_ID_PLX		0x10b5
+#define PCI_DEVICE_ID_PLX_9050      0x9050
 #define PCI_DEVICE_ID_PLX_9080		0x9080
 
+#define PCI_DEVICE_ID_PLX_SPCOM200  0x1103
+
 #define PCI_VENDOR_ID_MADGE		0x10b6
 #define PCI_DEVICE_ID_MADGE_MK2		0x0002
 
@@ -765,6 +768,7 @@
 #define PCI_DEVICE_ID_ATT_L56XMF	0x0440
 
 #define PCI_VENDOR_ID_SPECIALIX		0x11cb
+#define PCI_DEVICE_ID_SPECIALIX_IO8	0x2000
 #define PCI_DEVICE_ID_SPECIALIX_XIO	0x4000
 #define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
 
@@ -827,6 +831,9 @@
 #define PCI_DEVICE_ID_OPTIBASE_VPLEXCC	0x2120
 #define PCI_DEVICE_ID_OPTIBASE_VQUEST	0x2130
 
+#define PCI_VENDOR_ID_ASIX		0x125b
+#define PCI_DEVICE_ID_ASIX_88140	0x1400
+
 #define PCI_VENDOR_ID_SATSAGEM		0x1267
 #define PCI_DEVICE_ID_SATSAGEM_PCR2101	0x5352
 #define PCI_DEVICE_ID_SATSAGEM_TELSATTURBO 0x5a4b
@@ -920,6 +927,9 @@
 #define PCI_DEVICE_ID_INTEL_82443BX_0	0x7190
 #define PCI_DEVICE_ID_INTEL_82443BX_1	0x7191
 #define PCI_DEVICE_ID_INTEL_82443BX_2	0x7192
+#define PCI_DEVICE_ID_INTEL_82443GX_0	0x71A0
+#define PCI_DEVICE_ID_INTEL_82443GX_1	0x71A1
+#define PCI_DEVICE_ID_INTEL_82443GX_2	0x71A2
 #define PCI_DEVICE_ID_INTEL_P6		0x84c4
 #define PCI_DEVICE_ID_INTEL_82450GX	0x84c5
 
@@ -945,6 +955,12 @@
 #define PCI_DEVICE_ID_ADAPTEC_7883	0x8378
 #define PCI_DEVICE_ID_ADAPTEC_7884	0x8478
 #define PCI_DEVICE_ID_ADAPTEC_1030	0x8b78
+
+#define PCI_VENDOR_ID_ADAPTEC2		0x9005
+#define PCI_DEVICE_ID_ADAPTEC2_2940U2	0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_7890	0x001f
+#define PCI_DEVICE_ID_ADAPTEC2_3940U2	0x0050
+#define PCI_DEVICE_ID_ADAPTEC2_7896	0x005f
 
 #define PCI_VENDOR_ID_ATRONICS		0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015	0x2015
diff -u --recursive --new-file v2.0.35/linux/include/linux/personality.h linux/include/linux/personality.h
--- v2.0.35/linux/include/linux/personality.h	Mon Jul 13 13:46:42 1998
+++ linux/include/linux/personality.h	Sun Nov 15 10:33:18 1998
@@ -27,7 +27,7 @@
 #define PER_XENIX		(0x0007 | STICKY_TIMEOUTS)
 
 /* Prototype for an lcall7 syscall handler. */
-typedef asmlinkage void (*lcall7_func)(struct pt_regs *);
+typedef void (*lcall7_func)(struct pt_regs *);
 
 
 /* Description of an execution domain - personality range supported,
diff -u --recursive --new-file v2.0.35/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.0.35/linux/include/linux/proc_fs.h	Sun Nov 15 10:49:53 1998
+++ linux/include/linux/proc_fs.h	Sun Nov 15 10:33:18 1998
@@ -141,6 +141,7 @@
 	PROC_SCSI_AM53C974,
 	PROC_SCSI_SSC,
 	PROC_SCSI_NCR53C406A,
+	PROC_SCSI_MEGARAID,
 	PROC_SCSI_PPA,
 	PROC_SCSI_ESP,
 	PROC_SCSI_A3000,
diff -u --recursive --new-file v2.0.35/linux/include/linux/rose.h linux/include/linux/rose.h
--- v2.0.35/linux/include/linux/rose.h	Sun Nov 15 10:49:53 1998
+++ linux/include/linux/rose.h	Sun Nov 15 10:33:18 1998
@@ -7,9 +7,10 @@
 #ifndef	ROSE_KERNEL_H
 #define	ROSE_KERNEL_H
 
-#define PF_ROSE		AF_ROSE
 #define ROSE_MTU	251
 
+#define ROSE_MAX_DIGIS 6
+
 #define	ROSE_DEFER	1
 #define ROSE_T1		2
 #define	ROSE_T2		3
@@ -21,8 +22,11 @@
 #define	SIOCRSGCAUSE		(SIOCPROTOPRIVATE+0)
 #define	SIOCRSSCAUSE		(SIOCPROTOPRIVATE+1)
 #define	SIOCRSL2CALL		(SIOCPROTOPRIVATE+2)
+#define	SIOCRSSL2CALL		(SIOCPROTOPRIVATE+2)
 #define	SIOCRSACCEPT		(SIOCPROTOPRIVATE+3)
-#define	SIOCRSCLRRT		(SIOCPROTOPRIVATE+4)
+#define	SIOCRSCLRRT			(SIOCPROTOPRIVATE+4)
+#define	SIOCRSGL2CALL		(SIOCPROTOPRIVATE+5)
+#define	SIOCRSGFACILITIES	(SIOCPROTOPRIVATE+6)
 
 #define	ROSE_DTE_ORIGINATED	0x00
 #define	ROSE_NUMBER_BUSY	0x01
@@ -36,22 +40,30 @@
 #define	ROSE_SHIP_ABSENT	0x39
 
 typedef struct {
-	char		rose_addr[5];
+	char			rose_addr[5];
 } rose_address;
 
 struct sockaddr_rose {
 	unsigned short	srose_family;
 	rose_address	srose_addr;
 	ax25_address	srose_call;
-	int		srose_ndigis;
+	unsigned int	srose_ndigis;
 	ax25_address	srose_digi;
 };
 
+struct full_sockaddr_rose {
+	unsigned short	srose_family;
+	rose_address	srose_addr;
+	ax25_address	srose_call;
+	unsigned int	srose_ndigis;
+	ax25_address	srose_digis[ROSE_MAX_DIGIS];
+};
+
 struct rose_route_struct {
 	rose_address	address;
 	unsigned short	mask;
 	ax25_address	neighbour;
-	char		device[16];
+	char			device[16];
 	unsigned char	ndigis;
 	ax25_address	digipeaters[AX25_MAX_DIGIS];
 };
@@ -60,5 +72,17 @@
 	unsigned char	cause;
 	unsigned char	diagnostic;
 };
+
+struct rose_facilities_struct {
+	rose_address	source_addr,   dest_addr;
+	ax25_address	source_call,   dest_call;
+	unsigned char	source_ndigis, dest_ndigis;
+	ax25_address	source_digis[ROSE_MAX_DIGIS];
+	ax25_address	dest_digis[ROSE_MAX_DIGIS];
+	unsigned int	rand;
+	rose_address	fail_addr;
+	ax25_address	fail_call;
+};
+
 
 #endif
diff -u --recursive --new-file v2.0.35/linux/include/linux/serial.h linux/include/linux/serial.h
--- v2.0.35/linux/include/linux/serial.h	Mon Aug  4 15:01:09 1997
+++ linux/include/linux/serial.h	Sun Nov 15 10:33:18 1998
@@ -44,7 +44,8 @@
 #define PORT_16550A	4
 #define PORT_CIRRUS     5
 #define PORT_16650	6
-#define PORT_MAX	6
+#define PORT_STARTECH	7
+#define PORT_MAX	7
 
 /*
  * Definitions for async_struct (and serial_struct) flags field
diff -u --recursive --new-file v2.0.35/linux/include/linux/shm.h linux/include/linux/shm.h
--- v2.0.35/linux/include/linux/shm.h	Thu Aug 14 13:31:28 1997
+++ linux/include/linux/shm.h	Sun Nov 15 10:33:18 1998
@@ -63,6 +63,7 @@
 asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr);
 asmlinkage int sys_shmdt (char *shmaddr);
 asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
+extern void shm_unuse(unsigned int type);
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.0.35/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.0.35/linux/include/linux/skbuff.h	Wed Oct 15 15:22:08 1997
+++ linux/include/linux/skbuff.h	Sun Nov 15 10:33:18 1998
@@ -112,6 +112,17 @@
 	unsigned char 	*end;			/* End pointer					*/
 	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
 	__u16		redirport;		/* Redirect port				*/
+
+	/*
+	 *	Keep this at the end then we wont break stuff.
+	 */
+#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
+        __u32           shapelatency;           /* Latency on frame */
+        __u32           shapeclock;             /* Time it should go out */
+        __u32           shapelen;               /* Frame length in clocks */
+        __u32           shapestamp;             /* Stamp for shaper    */
+        __u16           shapepend;              /* Pending */
+#endif
 };
 
 #ifdef CONFIG_SKB_LARGE
diff -u --recursive --new-file v2.0.35/linux/include/linux/types.h linux/include/linux/types.h
--- v2.0.35/linux/include/linux/types.h	Tue Oct  8 09:43:30 1996
+++ linux/include/linux/types.h	Sun Nov 15 10:33:19 1998
@@ -1,6 +1,18 @@
 #ifndef _LINUX_TYPES_H
 #define _LINUX_TYPES_H
 
+#ifdef __i386__
+#if defined(__KERNEL__) && !defined(STDC_HEADERS)
+#if ((__GNUC_MINOR__ >= 8) || (__GNUC_MAJOR >=3))
+#warning "This code is tested with gcc 2.7.2.x only. Using egcs/gcc 2.8.x needs"
+#warning "additional patches that have not been sufficiently tested to include by"
+#warning "default."
+#warning "See http://www.suse.de/~florian/kernel+egcs.html for more information"
+#error "Remove this if you have applied the gcc 2.8/egcs patches and wish to use them"
+#endif
+#endif
+#endif
+
 #include <linux/posix_types.h>
 #include <asm/types.h>
 
diff -u --recursive --new-file v2.0.35/linux/include/linux/wireless.h linux/include/linux/wireless.h
--- v2.0.35/linux/include/linux/wireless.h	Tue Aug 12 15:01:27 1997
+++ linux/include/linux/wireless.h	Sun Nov 15 10:33:19 1998
@@ -245,7 +245,7 @@
 		/* Name : used to verify the presence of  wireless extensions.
 		 * Name of the protocol/provider... */
 
-		struct		/* network id (or domain) : used to to */
+		struct		/* network id (or domain) : used to */
 		{		/* create logical channels on the air */
 			__u32	nwid;		/* value */
 			__u8	on;		/* active/unactive nwid */
diff -u --recursive --new-file v2.0.35/linux/include/net/rose.h linux/include/net/rose.h
--- v2.0.35/linux/include/net/rose.h	Sun Nov 15 10:49:53 1998
+++ linux/include/net/rose.h	Sun Nov 15 10:33:19 1998
@@ -18,6 +18,7 @@
 #define	ROSE_Q_BIT			0x80
 #define	ROSE_D_BIT			0x40
 #define	ROSE_M_BIT			0x10
+#define	M_BIT				0x10
 
 #define	ROSE_CALL_REQUEST		0x0B
 #define	ROSE_CALL_ACCEPTED		0x0F
@@ -55,25 +56,30 @@
 #define	ROSE_DEFAULT_ROUTING		1			/* Default routing flag */
 #define	ROSE_DEFAULT_FAIL_TIMEOUT	(120 * ROSE_SLOWHZ)	/* Time until link considered usable */
 #define	ROSE_DEFAULT_MAXVC		50			/* Maximum number of VCs per neighbour */
-#define ROSE_DEFAULT_WINDOW_SIZE	3			/* Default window value */
+#define ROSE_DEFAULT_WINDOW_SIZE	7			/* Default window value */
 
 #define ROSE_MODULUS 			8
 #define	ROSE_MAX_PACKET_SIZE		251			/* Maximum Packet Size */
 
+#define ROSE_MAX_WINDOW_LEN		((AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 300) * 7)
+
 #define	ROSE_COND_ACK_PENDING		0x01
 #define	ROSE_COND_PEER_RX_BUSY		0x02
 #define	ROSE_COND_OWN_RX_BUSY		0x04
 
-#define	FAC_NATIONAL			0x00
-#define	FAC_CCITT			0x0F
+#define	FAC_NATIONAL				0x00
+#define	FAC_CCITT					0x0F
 
-#define	FAC_NATIONAL_RAND		0x7F
-#define	FAC_NATIONAL_FLAGS		0x3F
+#define	FAC_NATIONAL_RAND			0x7F
+#define	FAC_NATIONAL_FLAGS			0x3F
 #define	FAC_NATIONAL_DEST_DIGI		0xE9
 #define	FAC_NATIONAL_SRC_DIGI		0xEB
+#define	FAC_NATIONAL_FAIL_CALL		0xED
+#define	FAC_NATIONAL_FAIL_ADD		0xEE
+#define	FAC_NATIONAL_DIGIS			0xEF
 
-#define	FAC_CCITT_DEST_NSAP		0xC9
-#define	FAC_CCITT_SRC_NSAP		0xCB
+#define	FAC_CCITT_DEST_NSAP			0xC9
+#define	FAC_CCITT_SRC_NSAP			0xCB
 
 struct rose_neigh {
 	struct rose_neigh	*next;
@@ -91,28 +97,29 @@
 	struct timer_list	timer;
 };
 
+#define ROSE_MAX_ALTERNATE 3
 struct rose_node {
 	struct rose_node	*next;
 	rose_address		address;
 	unsigned short		mask;
 	unsigned char		count;
-	struct rose_neigh	*neighbour[3];
+	struct rose_neigh	*neighbour[ROSE_MAX_ALTERNATE];
 };
 
+typedef struct {
+	unsigned int 		lci;
+	struct rose_neigh	*neigh;
+	unsigned short		vs, vr, va, vl;
+	unsigned short		pending;
+	unsigned char		state, condition;
+	struct timer_list	timer;
+} rose_tr;
+
 struct rose_route {
 	struct rose_route	*next;
 	rose_address		src_addr, dest_addr;
 	ax25_address		src_call, dest_call;
-	unsigned int		lci1, lci2;
-	struct rose_neigh	*neigh1, *neigh2;
-	unsigned int		rand;
-};
-
-struct rose_facilities {
-	rose_address		source_addr,   dest_addr;
-	ax25_address		source_call,   dest_call;
-	unsigned char		source_ndigis, dest_ndigis;
-	ax25_address		source_digi,   dest_digi;
+	rose_tr				tr1, tr2;
 	unsigned int		rand;
 };
 
@@ -120,7 +127,8 @@
 	rose_address		source_addr,   dest_addr;
 	ax25_address		source_call,   dest_call;
 	unsigned char		source_ndigis, dest_ndigis;
-	ax25_address		source_digi,   dest_digi;
+	ax25_address		source_digis[ROSE_MAX_DIGIS];
+	ax25_address		dest_digis[ROSE_MAX_DIGIS];
 	struct rose_neigh	*neighbour;
 	struct device		*device;
 	unsigned int		lci, rand;
@@ -129,6 +137,12 @@
 	unsigned short		vs, vr, va, vl;
 	unsigned short		timer;
 	unsigned short		t1, t2, t3, hb;
+#ifdef M_BIT
+	unsigned short		fraglen;
+	struct sk_buff_head	frag_queue;
+#endif
+	struct sk_buff_head	ack_queue;
+	struct rose_facilities_struct facilities;
 	struct sock		*sk;		/* Backlink to socket */
 } rose_cb;
 
@@ -170,14 +184,19 @@
 extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char);
 extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
 
+/* rose_loopback.c */
+extern void rose_loopback_init(void);
+extern void rose_loopback_clear(void);
+extern int rose_loopback_queue(struct sk_buff *, struct rose_neigh *);
+
 /* rose_out.c */
 extern void rose_kick(struct sock *);
 extern void rose_enquiry_response(struct sock *);
-extern void rose_check_iframes_acked(struct sock *, unsigned short);
 
 /* rose_route.c */
 extern void rose_rt_device_down(struct device *);
 extern void rose_link_device_down(struct device *);
+extern void rose_clean_neighbour(struct rose_neigh *);
 extern struct device *rose_dev_first(void);
 extern struct device *rose_dev_get(rose_address *);
 extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *);
@@ -193,10 +212,11 @@
 
 /* rose_subr.c */
 extern void rose_clear_queues(struct sock *);
+extern void rose_frames_acked(struct sock *, unsigned short);
 extern int  rose_validate_nr(struct sock *, unsigned short);
 extern void rose_write_internal(struct sock *, int);
 extern int  rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
-extern int  rose_parse_facilities(struct sk_buff *, struct rose_facilities *);
+extern int  rose_parse_facilities(unsigned char *, struct rose_facilities_struct *);
 extern int  rose_create_facilities(unsigned char *, rose_cb *);
 
 /* rose_timer.c */
@@ -205,5 +225,9 @@
 /* sysctl_net_rose.c */
 extern void rose_register_sysctl(void);
 extern void rose_unregister_sysctl(void);
+
+/* rose_transit.c */
+void rose_transit(struct sk_buff *, rose_tr *, rose_tr *);
+void rose_init_transit(rose_tr *, unsigned int, struct rose_neigh *);
 
 #endif
diff -u --recursive --new-file v2.0.35/linux/include/net/sock.h linux/include/net/sock.h
--- v2.0.35/linux/include/net/sock.h	Sun Nov 15 10:49:53 1998
+++ linux/include/net/sock.h	Sun Nov 15 10:33:19 1998
@@ -350,7 +350,8 @@
    *	Moved solely for 2.0 to keep binary module compatibility stuff straight.
    */
    
-	unsigned short		max_ack_backlog;  
+	unsigned short		max_ack_backlog;
+	struct sock		*listening;
 };
 
 /*
diff -u --recursive --new-file v2.0.35/linux/init/main.c linux/init/main.c
--- v2.0.35/linux/init/main.c	Sun Nov 15 10:49:54 1998
+++ linux/init/main.c	Sun Nov 15 10:33:19 1998
@@ -36,6 +36,9 @@
 #ifdef CONFIG_ROOT_NFS
 #include <linux/nfs_fs.h>
 #endif
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
 
 #include <asm/bugs.h>
 
@@ -104,6 +107,7 @@
 extern void ppa_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
+extern void apm_setup(char *str, int *ints);
 extern void reboot_setup(char *str, int *ints);
 #ifdef CONFIG_CDU31A
 extern void cdu31a_setup(char *str, int *ints);
@@ -169,6 +173,9 @@
 extern void wd33c93_setup (char *str, int *ints);
 extern void gvp11_setup (char *str, int *ints);
 
+#ifdef CONFIG_CYCLADES
+extern void cy_setup(char *str, int *ints);
+#endif
 #ifdef CONFIG_DIGI
 extern void pcxx_setup(char *str, int *ints);
 #endif
@@ -446,6 +453,9 @@
 #if defined(CONFIG_GVP11_SCSI)
 	{ "gvp11=", gvp11_setup },
 #endif
+#ifdef CONFIG_CYCLADES
+	{ "cyclades=", cy_setup },
+#endif
 #ifdef CONFIG_DIGI
 	{ "digi=", pcxx_setup },
 #endif
@@ -458,6 +468,9 @@
 #ifdef CONFIG_BAYCOM
 	{ "baycom=", baycom_setup },
 #endif
+#ifdef CONFIG_APM
+	{ "apm=", apm_setup },
+#endif
 	{ 0, 0 }
 };
 
@@ -939,6 +952,11 @@
 	arch_syms_export();
 	sti();
 	check_bugs();
+
+#if defined(CONFIG_MTRR) && defined(__SMP__)
+	init_mtrr_config();
+#endif
+
 
 	printk(linux_banner);
 #ifdef __SMP__
diff -u --recursive --new-file v2.0.35/linux/ipc/shm.c linux/ipc/shm.c
--- v2.0.35/linux/ipc/shm.c	Mon Jul 13 13:46:42 1998
+++ linux/ipc/shm.c	Sun Nov 15 10:33:19 1998
@@ -3,6 +3,7 @@
  * Copyright (C) 1992, 1993 Krishna Balasubramanian
  *         Many improvements/fixes by Bruno Haible.
  * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
+ * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
  */
 
 #include <linux/errno.h>
@@ -657,6 +658,7 @@
 			oom(current);
 			return BAD_PAGE;
 		}
+	repeat:
 		pte_val(pte) = shp->shm_pages[idx];
 		if (pte_present(pte)) {
 			free_page (page); /* doesn't sleep */
@@ -664,11 +666,8 @@
 		}
 		if (!pte_none(pte)) {
 			read_swap_page(pte_val(pte), (char *) page);
-			pte_val(pte) = shp->shm_pages[idx];
-			if (pte_present(pte))  {
-				free_page (page); /* doesn't sleep */
-				goto done;
-			}
+			if (pte_val(pte) != shp->shm_pages[idx])
+				goto repeat;
 			swap_free(pte_val(pte));
 			shm_swp--;
 		}
@@ -698,6 +697,7 @@
 int shm_swap (int prio, int dma)
 {
 	pte_t page;
+	struct page *page_map;
 	struct shmid_ds *shp;
 	struct vm_area_struct *shmd;
 	unsigned long swap_nr;
@@ -732,7 +732,10 @@
 	pte_val(page) = shp->shm_pages[idx];
 	if (!pte_present(page))
 		goto check_table;
-	if (dma && !PageDMA(&mem_map[MAP_NR(pte_page(page))]))
+	page_map = &mem_map[MAP_NR(pte_page(page))];
+	if (PageLocked(page_map))
+		goto check_table;
+	if (dma && !PageDMA(page_map))
 		goto check_table;
 	swap_attempts++;
 
@@ -803,4 +806,66 @@
 	shm_swp++;
 	shm_rss--;
 	return 1;
+}
+
+/*
+ * Free the swap entry and set the new pte for the shm page.
+ */
+static void shm_unuse_page(struct shmid_ds *shp, unsigned long idx,
+			   unsigned long type)
+{
+	pte_t pte = __pte(shp->shm_pages[idx]);
+	unsigned long page, entry = shp->shm_pages[idx];
+
+	if (pte_none(pte))
+		return;
+	if (pte_present(pte))
+	{
+		/*
+		 * Security check. Should be not needed...
+		 */
+		unsigned long page_nr = MAP_NR(pte_page(pte));
+		if (page_nr >= MAP_NR(high_memory))
+		{
+			printk("shm page mapped in virtual memory\n");
+			return;
+		}
+		if (!in_swap_cache(page_nr))
+			return;
+		if (SWP_TYPE(in_swap_cache(page_nr)) != type)
+			return;
+		printk("shm page in swap cache, trying to remove it!\n");
+		delete_from_swap_cache(page_nr);
+		
+		shp->shm_pages[idx] = pte_val(pte_mkdirty(pte));
+		return;
+	}
+
+	if (SWP_TYPE(pte_val(pte)) != type)
+		return;
+
+	/*
+	 * Here we must swapin the pte and free the swap.
+	 */
+	page = get_free_page(GFP_KERNEL);
+	read_swap_page(pte_val(pte), (char *) page);
+	pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
+	shp->shm_pages[idx] = pte_val(pte);
+	shm_rss++;
+
+	swap_free(entry);
+	shm_swp--;
+}
+
+/*
+ * unuse_shm() search for an eventually swapped out shm page.
+ */
+void shm_unuse(unsigned int type)
+{
+	int i, n;
+
+	for (i = 0; i < SHMMNI; i++)
+		if (shm_segs[i] != IPC_UNUSED && shm_segs[i] != IPC_NOID)
+			for (n = 0; n < shm_segs[i]->shm_npages; n++)
+				shm_unuse_page(shm_segs[i], n, type);
 }
diff -u --recursive --new-file v2.0.35/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.0.35/linux/kernel/ksyms.c	Sun Nov 15 10:49:55 1998
+++ linux/kernel/ksyms.c	Sun Nov 15 10:33:20 1998
@@ -78,6 +78,9 @@
 
 extern void hard_reset_now(void);
 
+extern void select_free_wait(select_table * p);
+extern int select_check(int flag, select_table * wait, struct file * file);
+
 struct symbol_table symbol_table = {
 #include <linux/symtab_begin.h>
 #ifdef MODVERSIONS
@@ -160,6 +163,10 @@
 	X(getblk),
 	X(bread),
 	X(breada),
+
+	X(select_check),
+	X(select_free_wait),
+
 	X(__brelse),
 	X(__bforget),
 	X(ll_rw_block),
@@ -298,6 +305,7 @@
 	X(kill_proc),
 	X(kill_pg),
 	X(kill_sl),
+	X(force_sig),
 
 	/* misc */
 	X(panic),
@@ -310,6 +318,7 @@
 	X(sys_call_table),
 	X(hard_reset_now),
 	X(_ctype),
+	X(_ctmp),
 	X(get_random_bytes),
 
 	/* Signal interfaces */
@@ -368,6 +377,13 @@
 	X(get_write_access),
 	X(put_write_access),
 
+#ifdef CONFIG_PROC_FS
+	X(proc_dir_inode_operations),
+#endif
+
+	/* Modular sound */
+	X(sys_open),
+	X(sys_read),
 	/********************************************************
 	 * Do not add anything below this line,
 	 * as the stacked modules depend on this!
diff -u --recursive --new-file v2.0.35/linux/kernel/sched.c linux/kernel/sched.c
--- v2.0.35/linux/kernel/sched.c	Sun Nov 15 10:49:55 1998
+++ linux/kernel/sched.c	Sun Nov 15 10:33:20 1998
@@ -50,7 +50,7 @@
 
 long tick = (1000000 + HZ/2) / HZ;	/* timer interrupt period */
 volatile struct timeval xtime;		/* The current time */
-int tickadj = 500/HZ;			/* microsecs */
+int tickadj = 500/HZ ? 500/HZ : 1;	/* microsecs */
 
 DECLARE_TASK_QUEUE(tq_timer);
 DECLARE_TASK_QUEUE(tq_immediate);
diff -u --recursive --new-file v2.0.35/linux/kernel/sys.c linux/kernel/sys.c
--- v2.0.35/linux/kernel/sys.c	Mon Jul 13 13:46:42 1998
+++ linux/kernel/sys.c	Sun Nov 15 10:33:20 1998
@@ -197,10 +197,13 @@
 	else if (!flag)
 		C_A_D = 0;
 	else if (flag == 0xCDEF0123) {
+#ifdef CONFIG_SCSI_GDTH
+		gdth_halt();
+#endif
 		printk(KERN_EMERG "System halted\n");
 		sys_kill(-1, SIGKILL);
 #if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
-		apm_set_power_state(APM_STATE_OFF);
+		apm_power_off();
 #endif
 		do_exit(0);
 	} else
diff -u --recursive --new-file v2.0.35/linux/mm/mmap.c linux/mm/mmap.c
--- v2.0.35/linux/mm/mmap.c	Mon Jul 13 13:46:42 1998
+++ linux/mm/mmap.c	Sun Nov 15 10:33:20 1998
@@ -179,7 +179,7 @@
 			if (locks_verify_locked(file->f_inode))
 				return -EAGAIN;
 			/* cevans -- whoops another append-only file flaw */
-			if (IS_APPEND(file->f_inode) && (prot & PROT_WRITE))
+			if (IS_APPEND(file->f_inode) && (file->f_mode & 2))
 				return -EACCES;
 			/* fall through */
 		case MAP_PRIVATE:
diff -u --recursive --new-file v2.0.35/linux/mm/swapfile.c linux/mm/swapfile.c
--- v2.0.35/linux/mm/swapfile.c	Mon Jul 13 13:46:42 1998
+++ linux/mm/swapfile.c	Sun Nov 15 10:33:20 1998
@@ -5,6 +5,7 @@
  *  Swap reorganised 29.12.95, Stephen Tweedie
  */
 
+#include <linux/config.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/head.h>
@@ -17,6 +18,7 @@
 #include <linux/fs.h>
 #include <linux/swapctl.h>
 #include <linux/blkdev.h> /* for blk_size */
+#include <linux/shm.h>
 
 #include <asm/dma.h>
 #include <asm/system.h> /* for cli()/sti() */
@@ -313,6 +315,9 @@
 		nr++;
 	}
 	free_page(page);
+#ifdef CONFIG_SYSVIPC	
+	shm_unuse(type);
+#endif
 	return 0;
 }
 
diff -u --recursive --new-file v2.0.35/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.0.35/linux/mm/vmscan.c	Sun Nov 15 10:49:56 1998
+++ linux/mm/vmscan.c	Sun Nov 15 10:33:20 1998
@@ -415,7 +415,7 @@
 	switch (state) {
 		do {
 		case 0:
-			if (shrink_mmap(i, dma, can_do_io))
+			if (shrink_mmap(i, dma, 1))
 				return 1;
 			state = 1;
 		case 1:
diff -u --recursive --new-file v2.0.35/linux/net/bridge/br.c linux/net/bridge/br.c
--- v2.0.35/linux/net/bridge/br.c	Tue Aug 26 11:05:34 1997
+++ linux/net/bridge/br.c	Sun Nov 15 10:33:20 1998
@@ -921,10 +921,7 @@
 	skb->pkt_bridged = IS_BRIDGED;
 	skb->arp = 1;	/* do not resolve... */
 	skb->h.raw = skb->data + ETH_HLEN;
-	save_flags(flags);
-	cli();
-	skb_queue_tail(dev->buffs, skb);
-	restore_flags(flags);
+	dev_queue_xmit(skb, dev, SOPRI_INTERACTIVE);
 	return(0);
 }
 
@@ -977,10 +974,8 @@
 	skb->pkt_bridged = IS_BRIDGED;
 	skb->arp = 1;	/* do not resolve... */
 	skb->h.raw = skb->data + ETH_HLEN;
-	save_flags(flags);
-	cli();
-	skb_queue_tail(dev->buffs, skb);
-	restore_flags(flags);
+	
+	dev_queue_xmit(skb, dev, SOPRI_INTERACTIVE);
 	return(0);
 }
 
diff -u --recursive --new-file v2.0.35/linux/net/core/dev.c linux/net/core/dev.c
--- v2.0.35/linux/net/core/dev.c	Sun Nov 15 10:49:58 1998
+++ linux/net/core/dev.c	Sun Nov 15 10:33:20 1998
@@ -1507,6 +1507,8 @@
 extern int bpq_init(void);
 extern void sdla_setup(void);
 extern int dlci_setup(void);
+extern int sm_init(void);
+extern int baycom_init(void);
 
 int net_dev_init(void)
 {
@@ -1545,6 +1547,12 @@
 #endif
 #if defined(CONFIG_SDLA)
 	sdla_setup();
+#endif
+#if defined(CONFIG_BAYCOM)
+        baycom_init();
+#endif
+#if defined(CONFIG_SOUNDMODEM)
+        sm_init();
 #endif
 	/*
 	 *	SLHC if present needs attaching so other people see it
diff -u --recursive --new-file v2.0.35/linux/net/core/firewall.c linux/net/core/firewall.c
--- v2.0.35/linux/net/core/firewall.c	Sun May 19 13:40:50 1996
+++ linux/net/core/firewall.c	Sun Nov 15 10:33:21 1998
@@ -7,6 +7,7 @@
  */
 
 #include <linux/module.h> 
+#include <linux/config.h>
 #include <linux/skbuff.h>
 #include <linux/firewall.h>
 
diff -u --recursive --new-file v2.0.35/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.0.35/linux/net/core/skbuff.c	Tue Apr  8 08:47:47 1997
+++ linux/net/core/skbuff.c	Sun Nov 15 10:33:21 1998
@@ -865,6 +865,11 @@
 	if(skb->lock==0)
 		net_locked--;
 	restore_flags(flags);
+
+        if (skb->free == 3) {
+            skb->free = 1;
+            kfree_skb(skb, FREE_WRITE);
+        }
 }
 
 void dev_kfree_skb(struct sk_buff *skb, int mode)
diff -u --recursive --new-file v2.0.35/linux/net/core/sock.c linux/net/core/sock.c
--- v2.0.35/linux/net/core/sock.c	Mon Jul 13 13:46:43 1998
+++ linux/net/core/sock.c	Sun Nov 15 10:33:21 1998
@@ -241,6 +241,10 @@
  			/* 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(!suser())
+ 				return -EPERM;
+ 				
  			if (!valbool) {
  				sk->bound_device = NULL;
  			} else {
diff -u --recursive --new-file v2.0.35/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.0.35/linux/net/ipv4/arp.c	Sun Nov 15 10:49:59 1998
+++ linux/net/ipv4/arp.c	Sun Nov 15 10:33:21 1998
@@ -1771,6 +1771,12 @@
 	unsigned char *sha,*tha;
 	u32 sip,tip;
 
+	if(skb->pkt_type == PACKET_OTHERHOST)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	
 /*
  *	The hardware length of the packet should match the hardware length
  *	of the device.  Similarly, the hardware types should match.  The
diff -u --recursive --new-file v2.0.35/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.0.35/linux/net/ipv4/ip_input.c	Wed Sep 17 12:00:47 1997
+++ linux/net/ipv4/ip_input.c	Sun Nov 15 10:33:21 1998
@@ -251,14 +251,6 @@
 
 	ip_statistics.IpInReceives++;
 
-	/*
-	 *	Account for the packet (even if the packet is
-	 *	not accepted by the firewall!).
-	 */
-
-#ifdef CONFIG_IP_ACCT
-	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
-#endif	
 
 	/*
 	 *	Tag the ip header of this packet so we can find it
@@ -294,6 +286,24 @@
 	 */
 
 	skb_trim(skb,ntohs(iph->tot_len));
+	
+	if(skb->len < (iph->ihl<<2))
+	{
+		ip_statistics.IpInHdrErrors++;
+		kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	/*
+	 *	Account for the packet (even if the packet is
+	 *	not accepted by the firewall!). We do this after
+	 *	the sanity checks and the additional ihl check
+	 *	so we dont account garbage as we might do before.
+	 */
+
+#ifdef CONFIG_IP_ACCT
+	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
+#endif	
 
 	/*
 	 *	Try to select closest <src,dst> alias device, if any.
diff -u --recursive --new-file v2.0.35/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v2.0.35/linux/net/ipv4/ip_masq.c	Mon Jul 13 13:46:43 1998
+++ linux/net/ipv4/ip_masq.c	Sun Nov 15 10:33:21 1998
@@ -1515,32 +1515,35 @@
                 else
                 {
 			struct tcphdr *th;
-                        skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1),
+			if(len>=sizeof(struct tcphdr))
+			{
+	                        skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1),
                                                  len - sizeof(struct tcphdr), 0);
-                        tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb);
+                        	tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb);
 
-			/* Check if TCP FIN or RST */
-			th = (struct tcphdr *)portptr;
-			if (th->fin)
-			{
-				ms->flags |= IP_MASQ_F_SAW_FIN_IN;
-			}
-			if (th->rst)
-			{
-				ms->flags |= IP_MASQ_F_SAW_RST;
-			}
+				/* Check if TCP FIN or RST */
+				th = (struct tcphdr *)portptr;
+				if (th->fin)
+				{
+					ms->flags |= IP_MASQ_F_SAW_FIN_IN;
+				}
+				if (th->rst)
+				{
+					ms->flags |= IP_MASQ_F_SAW_RST;
+				}
 			
-			/* Now set the timeouts */
-			if (ms->flags & IP_MASQ_F_SAW_RST)
-			{
-				timeout = 1;
-			}
-			else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN)
-			{
-				timeout = ip_masq_expire->tcp_fin_timeout;
+				/* Now set the timeouts */
+				if (ms->flags & IP_MASQ_F_SAW_RST)
+				{
+					timeout = 1;
+				}
+				else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN)
+				{
+					timeout = ip_masq_expire->tcp_fin_timeout;
+				}
+				else timeout = ip_masq_expire->tcp_timeout;
 			}
-			else timeout = ip_masq_expire->tcp_timeout;
-                }
+                }	
 		ip_masq_set_expire(ms, timeout);
                 ip_send_check(iph);
 #ifdef DEBUG_CONFIG_IP_MASQUERADE
diff -u --recursive --new-file v2.0.35/linux/net/ipv4/ip_masq_quake.c linux/net/ipv4/ip_masq_quake.c
--- v2.0.35/linux/net/ipv4/ip_masq_quake.c	Tue Dec  2 13:52:33 1997
+++ linux/net/ipv4/ip_masq_quake.c	Sun Nov 15 10:33:21 1998
@@ -20,7 +20,6 @@
  *  
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/types.h>
diff -u --recursive --new-file v2.0.35/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.0.35/linux/net/ipv4/tcp.c	Mon Jul 13 13:46:43 1998
+++ linux/net/ipv4/tcp.c	Sun Nov 15 10:33:21 1998
@@ -516,7 +516,7 @@
  * load is kept track of, if it is zero there is a strong
  * likely hood that there is a zero length chain we will
  * find with a small amount of searching, else the load is
- * what we shoot for for when the chains all have at least
+ * what we shoot for when the chains all have at least
  * one entry.  The base helps us walk the chains in an
  * order such that a good chain is found as quickly as possible.  -DaveM
  */
@@ -943,7 +943,7 @@
 		return 1;
 
 	case SEL_EX:
-		if (sk->urg_data)
+		if (sk->urg_data & URG_VALID)
 			return 1;
 		break;
 	}
@@ -1976,8 +1976,15 @@
 
 	if(sk->state == TCP_LISTEN)
 	{
-		/* Special case */
+		/* 
+		 *	Special case 
+		 */
 		tcp_set_state(sk, TCP_CLOSE);
+		/*
+		 *	Our children must die before we do now that
+		 *	sk->listening exists. It was right anyway but
+		 *	dont break this assumption.
+		 */
 		tcp_close_pending(sk);
 		release_sock(sk);
 		sk->dead = 1;
@@ -2046,11 +2053,12 @@
 			tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT);
 	}
 
-	sk->dead = 1;
 	release_sock(sk);
 
 	if(sk->state == TCP_CLOSE)
 		tcp_v4_unhash(sk);
+
+	sk->dead = 1;
 }
 
 
diff -u --recursive --new-file v2.0.35/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.0.35/linux/net/ipv4/tcp_input.c	Sun Nov 15 10:49:59 1998
+++ linux/net/ipv4/tcp_input.c	Sun Nov 15 10:33:22 1998
@@ -44,6 +44,8 @@
  *					then use it for ToS is even more 
  *					broken. 
  *					</RANT>
+ *	George Baeslack		:	SIGIO delivery on accept() bug that
+ *					affected sun jdk.
  */
 
 #include <linux/config.h>
@@ -544,7 +546,10 @@
 	/* If the socket is dead, don't accept the connection. */
 	if (!sk->dead) 
 	{
-  		sk->data_ready(sk,0);
+		/*
+		 * This must wait for 3 way completion.
+  		 * sk->data_ready(sk,0);
+  		 */
 	}
 	else 
 	{
@@ -772,6 +777,7 @@
 	newsk->acked_seq = skb->seq + 1;
 	newsk->copied_seq = skb->seq + 1;
 	newsk->socket = NULL;
+	newsk->listening = sk;
 
 	/*
 	 *	Grab the ttl and tos values and use them 
@@ -886,7 +892,7 @@
 	/* If the socket is dead, don't accept the connection. */
 	if (!sk->dead) 
 	{
-  		sk->data_ready(sk,0);
+  		/*sk->data_ready(sk,0); */
 	}
 	else 
 	{
@@ -1032,6 +1038,7 @@
 	newsk->acked_seq = skb->seq;
 	newsk->copied_seq = skb->seq;
 	newsk->socket = NULL;
+	newsk->listening = sk;
 
 	/*
 	 *	Grab the ttl and tos values and use them 
@@ -1663,6 +1670,19 @@
 	if (sk->state==TCP_SYN_RECV)
 	{
 		tcp_set_state(sk, TCP_ESTABLISHED);
+		
+		/*
+		 *	We have a listening socket owning us. Wake it for
+		 *	the accept.
+		 */
+		 
+  		if ( sk->listening ) 
+  		{
+  			/* The listener may be sk->dead. Dont worry 
+  			   data_ready traps this */
+			sk->data_ready(sk->listening,0);
+			sk->listening = NULL;
+		}			
 
 		/* Must check for peer advertising zero sized window
 		 * or else we get a sk->{mtu,mss} of zero and thus bomb out
@@ -2496,10 +2516,14 @@
 		 *	then it's a new connection
 		 */
 		 
-		if (sk->state == TCP_SYN_RECV && th->syn && skb->seq+1 == sk->acked_seq)
+		if (sk->state == TCP_SYN_RECV)
 		{
-			kfree_skb(skb, FREE_READ);
-			return 0;
+			if(th->syn && skb->seq+1 == sk->acked_seq)
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			goto rfc_step4;
 		}
 		
 		/*
@@ -2588,6 +2612,7 @@
 				sk->rtt = 0;
 				sk->rto = TCP_TIMEOUT_INIT;
 				sk->mdev = TCP_TIMEOUT_INIT;
+				goto rfc_step6;
 			}
 			else
 			{
@@ -2614,10 +2639,11 @@
 				kfree_skb(skb, FREE_READ);
 				return 0;
 			}
+
 			/*
-			 *	SYN_RECV with data maybe.. drop through
+			 *	Data maybe.. drop through
 			 */
-			goto rfc_step6;
+			 
 		}
 
 	/*
@@ -2676,6 +2702,8 @@
 #endif	
 	}
 
+rfc_step4:		/* I'll clean this up later */
+
 	/*
 	 *	We are now in normal data flow (see the step list in the RFC)
 	 *	Note most of these are inline now. I'll inline the lot when
@@ -2717,8 +2745,13 @@
 	 *	Process the ACK
 	 */
 	 
+	if(!th->ack)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
 
-	if(th->ack && !tcp_ack(sk,th,skb->ack_seq,len))
+	if(!tcp_ack(sk,th,skb->ack_seq,len))
 	{
 		/*
 		 *	Our three way handshake failed.
@@ -2732,8 +2765,7 @@
 		return 0;
 	}
 	
-rfc_step6:		/* I'll clean this up later */
-
+rfc_step6:
 	/*
 	 *	If the accepted buffer put us over our queue size we
 	 *	now drop it (we must process the ack first to avoid
diff -u --recursive --new-file v2.0.35/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.0.35/linux/net/ipv4/tcp_output.c	Sun Nov 15 10:49:59 1998
+++ linux/net/ipv4/tcp_output.c	Sun Nov 15 10:33:22 1998
@@ -858,7 +858,7 @@
 	struct tcphdr *th =(struct tcphdr *)&sk->dummy_th;
 	struct tcphdr *t1;
 	struct sk_buff *buff;
-	struct device *dev=NULL;
+	struct device *dev=sk->bound_device;
 	int tmp;
 		
 	buff = sock_wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL);
@@ -962,7 +962,7 @@
 	struct tcphdr *t1;
 	unsigned char *ptr;
 	struct sk_buff * buff;
-	struct device *ndev=NULL;
+	struct device *ndev=newsk->bound_device;
 	int tmp;
 
 	buff = sock_wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
@@ -1121,7 +1121,7 @@
 {
 	struct sk_buff *buff;
 	struct tcphdr *t1;
-	struct device *dev = NULL;
+	struct device *dev = sk->bound_device;
 	int tmp;
 
 	if(sk->zapped)
@@ -1216,7 +1216,7 @@
 {
 	struct sk_buff *buff,*skb;
 	struct tcphdr *t1;
-	struct device *dev=NULL;
+	struct device *dev=sk->bound_device;
 	int tmp;
 
 	if (sk->zapped)
diff -u --recursive --new-file v2.0.35/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.0.35/linux/net/netrom/af_netrom.c	Sun Nov 15 10:49:59 1998
+++ linux/net/netrom/af_netrom.c	Sun Nov 15 10:33:22 1998
@@ -237,7 +237,7 @@
 /*
  *	Find a connected NET/ROM socket given their circuit IDs.
  */
-static struct sock *nr_find_peer(unsigned char index, unsigned char id)
+static struct sock *nr_find_peer(unsigned char index, unsigned char id, ax25_address *dest)
 {
 	struct sock *s;
 	unsigned long flags;
@@ -246,7 +246,7 @@
 	cli();
 
 	for (s = nr_list; s != NULL; s = s->next) {
-		if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id) {
+		if (s->protinfo.nr->your_index == index && s->protinfo.nr->your_id == id && ax25cmp(&s->protinfo.nr->dest_addr, dest) == 0) {
 			restore_flags(flags);
 			return s;
 		}
@@ -936,10 +936,10 @@
 
 	if (circuit_index == 0 && circuit_id == 0) {
 		if (frametype == NR_CONNACK && flags == NR_CHOKE_FLAG)
-			sk = nr_find_peer(peer_circuit_index, peer_circuit_id);
+			sk = nr_find_peer(peer_circuit_index, peer_circuit_id, src);
 	} else {
 		if (frametype == NR_CONNREQ)
-			sk = nr_find_peer(circuit_index, circuit_id);
+			sk = nr_find_peer(circuit_index, circuit_id, src);
 		else
 			sk = nr_find_socket(circuit_index, circuit_id);
 	}
diff -u --recursive --new-file v2.0.35/linux/net/netrom/nr_loopback.c linux/net/netrom/nr_loopback.c
--- v2.0.35/linux/net/netrom/nr_loopback.c	Sun Nov 15 10:49:59 1998
+++ linux/net/netrom/nr_loopback.c	Sun Nov 15 10:33:22 1998
@@ -11,6 +11,8 @@
  *
  *	History
  *	NET/ROM 006	Tomi(OH2BNS)	Created this file.
+ *					Changed the way the loopback
+ *					queue is consumed.
  *
  */
 
@@ -53,15 +55,16 @@
 {
 	struct sk_buff *skbn;
 
-	skbn = skb_copy(skb, GFP_ATOMIC);
+	skbn = skb_clone(skb, GFP_ATOMIC);
 
 	kfree_skb(skb, FREE_WRITE);
 
-	if (skbn != NULL)
+	if (skbn != NULL) {
 		skb_queue_tail(&loopback_queue, skbn);
 
-	if (!nr_loopback_running())
-		nr_set_loopback_timer();
+		if (!nr_loopback_running())
+			nr_set_loopback_timer();
+	}
 
 	return 1;
 }
@@ -85,16 +88,16 @@
 	ax25_address *nr_dest;
 	struct device *dev;
 
-	while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
+	if ((skb = skb_dequeue(&loopback_queue)) != NULL) {
 		nr_dest = (ax25_address *)(skb->data + 7);
 
-		if ((dev = nr_dev_get(nr_dest)) == NULL) {
-			kfree_skb(skb, FREE_READ);
-			continue;
-		}
+		dev = nr_dev_get(nr_dest);
 
-		if (nr_rx_frame(skb, dev) == 0)
+		if (dev == NULL || nr_rx_frame(skb, dev) == 0)
 			kfree_skb(skb, FREE_READ);
+
+		if (!skb_queue_empty(&loopback_queue) && !nr_loopback_running())
+			nr_set_loopback_timer();
 	}
 }
 
diff -u --recursive --new-file v2.0.35/linux/net/rose/Makefile linux/net/rose/Makefile
--- v2.0.35/linux/net/rose/Makefile	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/Makefile	Sun Nov 15 10:33:22 1998
@@ -8,7 +8,8 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := rose.o
-O_OBJS	 := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o
+O_OBJS	 := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o \
+rose_loopback.o rose_out.o rose_route.o rose_subr.o rose_timer.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.0.35/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.0.35/linux/net/rose/af_rose.c	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/af_rose.c	Sun Nov 15 10:33:22 1998
@@ -1,5 +1,5 @@
 /*
- *	ROSE release 003
+ *	ROSE release 006
  *
  *	This code REQUIRES 2.1.0 or higher/ NET3.029
  *
@@ -14,8 +14,13 @@
  *			Terry (VK2KTJ)	Added support for variable length
  *					address masks.
  *	ROSE 002	Jonathan(G4KLX)	Changed hdrincl to qbitincl.
- *					Added random number facilities entry.
+ *	            Added random number facilities entry.
  *	ROSE 003	Jonathan(G4KLX)	Added use count to neighbour.
+ *	ROSE 004	Jean-Paul(F6FBB) Added LoopBack, M-Bit and
+ *					FPAC facilities.
+ *  ROSE 005    Jean-Paul(F6FBB) Added rose_clean_neighbour
+ *  ROSE 006    Jean-Paul(F6FBB) Accept up to 6 digis
+ *              Fixed a possible loop in facilities
  */
 
 #include <linux/config.h>
@@ -198,6 +203,7 @@
 
 	for (s = rose_list; s != NULL; s = s->next) {
 		if (s->protinfo.rose->neighbour == neigh) {
+			rose_clear_queues(s); /* F6FBB */
 			s->protinfo.rose->cause      = ROSE_OUT_OF_ORDER;
 			s->protinfo.rose->diagnostic = 0;
 			s->protinfo.rose->state     = ROSE_STATE_0;
@@ -221,6 +227,7 @@
 	
 	for (s = rose_list; s != NULL; s = s->next) {
 		if (s->protinfo.rose->device == dev) {
+			rose_clear_queues(s); /* F6FBB */
 			s->protinfo.rose->cause  = ROSE_OUT_OF_ORDER;
 			s->protinfo.rose->diagnostic = 0;
 			s->protinfo.rose->state  = ROSE_STATE_0;
@@ -542,7 +549,7 @@
 		sk->protinfo.rose->dest_ndigis = 0;
 		memset(&sk->protinfo.rose->dest_addr, '\0', ROSE_ADDR_LEN);
 		memset(&sk->protinfo.rose->dest_call, '\0', AX25_ADDR_LEN);
-		memset(&sk->protinfo.rose->dest_digi, '\0', AX25_ADDR_LEN);
+		memset(sk->protinfo.rose->dest_digis, '\0', AX25_ADDR_LEN*ROSE_MAX_DIGIS);
 		sk->max_ack_backlog = backlog;
 		sk->state           = TCP_LISTEN;
 		return 0;
@@ -579,7 +586,11 @@
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->back_log);
-
+	skb_queue_head_init(&rose->ack_queue);
+#ifdef M_BIT
+	skb_queue_head_init(&rose->frag_queue);
+	rose->fraglen    = 0;
+#endif
 	init_timer(&sk->timer);
 
 	sk->socket        = sock;
@@ -629,6 +640,11 @@
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->back_log);
+	skb_queue_head_init(&rose->ack_queue);
+#ifdef M_BIT
+	skb_queue_head_init(&rose->frag_queue);
+	rose->fraglen  = 0;
+#endif
 
 	init_timer(&sk->timer);
 
@@ -728,16 +744,23 @@
 	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
 	struct device *dev;
 	ax25_address *user, *source;
+	int n;
 
 	if (sk->zapped == 0)
 		return -EINVAL;
 
-	if (addr_len != sizeof(struct sockaddr_rose))
+	if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose))
 		return -EINVAL;
 
 	if (addr->srose_family != AF_ROSE)
 		return -EINVAL;
 
+	if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1)
+		return -EINVAL;
+
+	if (addr->srose_ndigis > ROSE_MAX_DIGIS)
+		return -EINVAL;
+
 	if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
 		if (sk->debug)
 			printk("ROSE: bind failed: invalid address\n");
@@ -752,13 +775,19 @@
 		user = source;
 	}
 
-	sk->protinfo.rose->source_addr = addr->srose_addr;
-	sk->protinfo.rose->source_call = *user;
-	sk->protinfo.rose->device      = dev;
-
-	if (addr->srose_ndigis == 1) {
-		sk->protinfo.rose->source_ndigis = 1;
-		sk->protinfo.rose->source_digi   = addr->srose_digi;
+	sk->protinfo.rose->source_addr   = addr->srose_addr;
+	sk->protinfo.rose->source_call   = *user;
+	sk->protinfo.rose->device        = dev;
+	sk->protinfo.rose->source_ndigis = addr->srose_ndigis;
+
+	if (addr_len == sizeof(struct full_sockaddr_rose)) {
+		struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr;
+		for (n = 0 ; n < addr->srose_ndigis ; n++)
+			sk->protinfo.rose->source_digis[n] = full_addr->srose_digis[n];
+	} else {
+		if (sk->protinfo.rose->source_ndigis == 1) {
+			sk->protinfo.rose->source_digis[0] = addr->srose_digi;
+		}
 	}
 
 	rose_insert_socket(sk);
@@ -778,6 +807,7 @@
 	unsigned char cause, diagnostic;
 	ax25_address *user;
 	struct device *dev;
+	int n;
 
 	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
@@ -795,14 +825,27 @@
 	sk->state   = TCP_CLOSE;
 	sock->state = SS_UNCONNECTED;
 
-	if (addr_len != sizeof(struct sockaddr_rose))
+	if (addr_len != sizeof(struct sockaddr_rose) && addr_len != sizeof(struct full_sockaddr_rose))
 		return -EINVAL;
 
 	if (addr->srose_family != AF_ROSE)
 		return -EINVAL;
 
-	if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL)
+	if (addr_len == sizeof(struct sockaddr_rose) && addr->srose_ndigis > 1)
+		return -EINVAL;
+
+	if (addr->srose_ndigis > ROSE_MAX_DIGIS)
+		return -EINVAL;
+
+	/* Source + Destination digis should not exceed ROSE_MAX_DIGIS */
+	if ((sk->protinfo.rose->source_ndigis + addr->srose_ndigis) > ROSE_MAX_DIGIS)
+		return -EINVAL;
+
+	if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL) {
+		sk->protinfo.rose->cause = cause;
+		sk->protinfo.rose->diagnostic = diagnostic;
 		return -ENETUNREACH;
+	}
 
 	if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0)
 		return -ENETUNREACH;
@@ -823,13 +866,19 @@
 		rose_insert_socket(sk);		/* Finish the bind */
 	}
 
-	sk->protinfo.rose->dest_addr = addr->srose_addr;
-	sk->protinfo.rose->dest_call = addr->srose_call;
-	sk->protinfo.rose->rand      = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci;
-
-	if (addr->srose_ndigis == 1) {
-		sk->protinfo.rose->dest_ndigis = 1;
-		sk->protinfo.rose->dest_digi   = addr->srose_digi;
+	sk->protinfo.rose->dest_addr   = addr->srose_addr;
+	sk->protinfo.rose->dest_call   = addr->srose_call;
+	sk->protinfo.rose->rand        = ((int)sk->protinfo.rose & 0xFFFF) + sk->protinfo.rose->lci;
+	sk->protinfo.rose->dest_ndigis = addr->srose_ndigis;
+
+	if (addr_len == sizeof(struct full_sockaddr_rose)) {
+		struct full_sockaddr_rose *full_addr = (struct full_sockaddr_rose *)uaddr;
+		for (n = 0 ; n < addr->srose_ndigis ; n++)
+			sk->protinfo.rose->dest_digis[n] = full_addr->srose_digis[n];
+	} else {
+		if (sk->protinfo.rose->dest_ndigis == 1) {
+			sk->protinfo.rose->dest_digis[0] = addr->srose_digi;
+		}
 	}
 
 	/* Move to connecting socket, start sending Connect Requests */
@@ -937,29 +986,44 @@
 {
 	struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr;
 	struct sock *sk = (struct sock *)sock->data;
+	int n;
 
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
 			return -ENOTCONN;
 		srose->srose_family = AF_ROSE;
-		srose->srose_ndigis = 0;
 		srose->srose_addr   = sk->protinfo.rose->dest_addr;
 		srose->srose_call   = sk->protinfo.rose->dest_call;
-		if (sk->protinfo.rose->dest_ndigis == 1) {
-			srose->srose_ndigis = 1;
-			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		srose->srose_ndigis = sk->protinfo.rose->dest_ndigis;
+		if (*uaddr_len >= sizeof(struct full_sockaddr_rose)) {
+			struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)uaddr;
+			for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++)
+				full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n];
+			*uaddr_len = sizeof(struct full_sockaddr_rose);
+		} else {
+			if (sk->protinfo.rose->dest_ndigis >= 1) {
+				srose->srose_ndigis = 1;
+				srose->srose_digi = sk->protinfo.rose->dest_digis[0];
+			}
+			*uaddr_len = sizeof(struct sockaddr_rose);
 		}
-		*uaddr_len = sizeof(struct sockaddr_rose);
 	} else {
 		srose->srose_family = AF_ROSE;
-		srose->srose_ndigis = 0;
 		srose->srose_addr   = sk->protinfo.rose->source_addr;
 		srose->srose_call   = sk->protinfo.rose->source_call;
-		if (sk->protinfo.rose->source_ndigis == 1) {
-			srose->srose_ndigis = 1;
-			srose->srose_digi   = sk->protinfo.rose->source_digi;
+		srose->srose_ndigis = sk->protinfo.rose->source_ndigis;
+		if (*uaddr_len >= sizeof(struct full_sockaddr_rose)) {
+			struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)uaddr;
+			for (n = 0 ; n < sk->protinfo.rose->source_ndigis ; n++)
+				full_srose->srose_digis[n] = sk->protinfo.rose->source_digis[n];
+			*uaddr_len = sizeof(struct full_sockaddr_rose);
+		} else {
+			if (sk->protinfo.rose->source_ndigis >= 1) {
+				srose->srose_ndigis = 1;
+				srose->srose_digi = sk->protinfo.rose->source_digis[sk->protinfo.rose->source_ndigis-1];
+			}
+			*uaddr_len = sizeof(struct sockaddr_rose);
 		}
-		*uaddr_len = sizeof(struct sockaddr_rose);
 	}
 
 	return 0;
@@ -969,14 +1033,21 @@
 {
 	struct sock *sk;
 	struct sock *make;
-	struct rose_facilities facilities;
-
+	struct rose_facilities_struct facilities;
+	int n, len;
+	
 	skb->sk = NULL;		/* Initially we don't know who it's for */
 
 	/*
 	 *	skb->data points to the rose frame start
 	 */
-	if (!rose_parse_facilities(skb, &facilities)) {
+
+	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
+	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
+
+	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
+	
+	if (!rose_parse_facilities(skb->data + len + 4, &facilities)) {
 		rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76);
 		return 0;
 	}
@@ -998,13 +1069,16 @@
 	make->protinfo.rose->dest_addr     = facilities.dest_addr;
 	make->protinfo.rose->dest_call     = facilities.dest_call;
 	make->protinfo.rose->dest_ndigis   = facilities.dest_ndigis;
-	make->protinfo.rose->dest_digi     = facilities.dest_digi;
+	for (n = 0 ; n < facilities.dest_ndigis ; n++)
+		make->protinfo.rose->dest_digis[n] = facilities.dest_digis[n];
 	make->protinfo.rose->source_addr   = facilities.source_addr;
 	make->protinfo.rose->source_call   = facilities.source_call;
 	make->protinfo.rose->source_ndigis = facilities.source_ndigis;
-	make->protinfo.rose->source_digi   = facilities.source_digi;
+	for (n = 0 ; n < facilities.source_ndigis ; n++)
+		make->protinfo.rose->source_digis[n]= facilities.source_digis[n];
 	make->protinfo.rose->neighbour     = neigh;
 	make->protinfo.rose->device        = dev;
+	make->protinfo.rose->facilities    = facilities;
 
 	make->protinfo.rose->neighbour->use++;
 
@@ -1040,10 +1114,10 @@
 	struct sock *sk = (struct sock *)sock->data;
 	struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name;
 	int err;
-	struct sockaddr_rose srose;
+	struct full_sockaddr_rose srose;
 	struct sk_buff *skb;
 	unsigned char *asmptr;
-	int size, qbit = 0;
+	int n, size, qbit = 0;
 
 	if (sk->err)
 		return sock_error(sk);
@@ -1063,15 +1137,19 @@
 		return -ENETUNREACH;
 
 	if (usrose != NULL) {
-		if (msg->msg_namelen < sizeof(srose))
+		if (msg->msg_namelen != sizeof(struct sockaddr_rose) && msg->msg_namelen != sizeof(struct full_sockaddr_rose))
 			return -EINVAL;
-		srose = *usrose;
+		memset(&srose, 0, sizeof(struct full_sockaddr_rose));
+		memcpy(&srose, usrose, msg->msg_namelen);
 		if (rosecmp(&sk->protinfo.rose->dest_addr, &srose.srose_addr) != 0 ||
 		    ax25cmp(&sk->protinfo.rose->dest_call, &srose.srose_call) != 0)
 			return -EISCONN;
-		if (srose.srose_ndigis == 1 && sk->protinfo.rose->dest_ndigis == 1) {
-			if (ax25cmp(&sk->protinfo.rose->dest_digi, &srose.srose_digi) != 0)
-				return -EISCONN;
+		if (srose.srose_ndigis != sk->protinfo.rose->dest_ndigis)
+			return -EISCONN;
+		if (srose.srose_ndigis == sk->protinfo.rose->dest_ndigis) {
+			for (n = 0 ; n < srose.srose_ndigis ; n++)
+				if (ax25cmp(&sk->protinfo.rose->dest_digis[n], &srose.srose_digis[n]) != 0)
+					return -EISCONN;
 		}
 		if (srose.srose_family != AF_ROSE)
 			return -EINVAL;
@@ -1082,12 +1160,9 @@
 		srose.srose_family = AF_ROSE;
 		srose.srose_addr   = sk->protinfo.rose->dest_addr;
 		srose.srose_call   = sk->protinfo.rose->dest_call;
-		srose.srose_ndigis = 0;
-
-		if (sk->protinfo.rose->dest_ndigis == 1) {
-			srose.srose_ndigis = 1;
-			srose.srose_digi   = sk->protinfo.rose->dest_digi;
-		}
+		srose.srose_ndigis = sk->protinfo.rose->dest_ndigis;
+		for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++)
+			srose.srose_digis[n] = sk->protinfo.rose->dest_digis[n];
 	}
 
 	if (sk->debug)
@@ -1154,10 +1229,55 @@
 		return -ENOTCONN;
 	}
 
-	skb_queue_tail(&sk->write_queue, skb);	/* Shove it onto the queue */
+#ifdef M_BIT
+#define ROSE_PACLEN (256-ROSE_MIN_LEN)
+	if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) {
+		unsigned char header[ROSE_MIN_LEN];
+		struct sk_buff *skbn;
+		int frontlen;
+		int lg;
+		
+		/* Save a copy of the Header */
+		memcpy(header, skb->data, ROSE_MIN_LEN);
+		skb_pull(skb, ROSE_MIN_LEN);
 
-	if (sk->protinfo.rose->state == ROSE_STATE_3)
-		rose_kick(sk);
+		frontlen = skb_headroom(skb);
+
+		while (skb->len > 0) {
+			if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, 0, &err)) == NULL)
+				return err;
+
+			skbn->sk   = sk;
+			skbn->free = 1;
+			skbn->arp  = 1;
+
+			skb_reserve(skbn, frontlen);
+
+			lg = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN;
+
+			/* Copy the user data */
+			memcpy(skb_put(skbn, lg), skb->data, lg);
+			skb_pull(skb, lg);
+
+			/* Duplicate the Header */
+			skb_push(skbn, ROSE_MIN_LEN);
+			memcpy(skbn->data, header, ROSE_MIN_LEN);
+
+			if (skb->len > 0)
+				skbn->data[2] |= M_BIT;
+		
+			skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
+		}
+		
+		skb->free = 1;
+		kfree_skb(skb, FREE_WRITE);
+	} else {
+		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
+	}
+#else
+	skb_queue_tail(&sk->write_queue, skb);	/* Shove it onto the queue */
+#endif
+	rose_kick(sk);
 
 	return len;
 }
@@ -1171,7 +1291,7 @@
 	int copied, qbit;
 	unsigned char *asmptr;
 	struct sk_buff *skb;
-	int er;
+	int n, er;
 
 	if (sk->err)
 		return sock_error(sk);
@@ -1208,14 +1328,19 @@
 		srose->srose_family = AF_ROSE;
 		srose->srose_addr   = sk->protinfo.rose->dest_addr;
 		srose->srose_call   = sk->protinfo.rose->dest_call;
-		srose->srose_ndigis = 0;
-
-		if (sk->protinfo.rose->dest_ndigis == 1) {
-			srose->srose_ndigis = 1;
-			srose->srose_digi   = sk->protinfo.rose->dest_digi;
+		srose->srose_ndigis = sk->protinfo.rose->dest_ndigis;
+		if (*addr_len >= sizeof(struct full_sockaddr_rose)) {
+			struct full_sockaddr_rose *full_srose = (struct full_sockaddr_rose *)msg->msg_name;
+			for (n = 0 ; n < sk->protinfo.rose->dest_ndigis ; n++)
+				full_srose->srose_digis[n] = sk->protinfo.rose->dest_digis[n];
+			*addr_len = sizeof(struct full_sockaddr_rose);
+		} else {
+			if (sk->protinfo.rose->dest_ndigis >= 1) {
+				srose->srose_ndigis = 1;
+				srose->srose_digi = sk->protinfo.rose->dest_digis[0];
+			}
+			*addr_len = sizeof(struct sockaddr_rose);
 		}
-
-		*addr_len = sizeof(*srose);
 	}
 
 	skb_free_datagram(sk, skb);
@@ -1313,7 +1438,7 @@
 			return 0;
 		}
 
-		case SIOCRSL2CALL:
+		case SIOCRSSL2CALL:
 			if (!suser()) return -EPERM;
 			if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0)
 				return err;
@@ -1324,6 +1449,12 @@
 				ax25_listen_register(&rose_callsign, NULL);
 			return 0;
 
+		case SIOCRSGL2CALL:
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(ax25_address))) != 0)
+				return err;
+			memcpy_tofs((void *)arg, &rose_callsign, sizeof(ax25_address));
+			return 0;
+
 		case SIOCRSACCEPT:
 			if (sk->protinfo.rose->state == ROSE_STATE_5) {
 				rose_write_internal(sk, ROSE_CALL_ACCEPTED);
@@ -1336,6 +1467,13 @@
 			}
 			return 0;
 
+		case SIOCRSGFACILITIES: {
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct rose_facilities_struct))) != 0)
+				return err;
+			memcpy_tofs((void *)arg, &sk->protinfo.rose->facilities, sizeof(struct rose_facilities_struct));
+			return 0;
+		}
+
 		default:
 			return dev_ioctl(cmd, (void *)arg);
 	}
@@ -1355,7 +1493,7 @@
 
 	cli();
 
-	len += sprintf(buffer, "dest_addr  dest_call src_addr   src_call  dev   lci st vs vr va   t  t1  t2  t3  hb    idle Snd-Q Rcv-Q Inode\n");
+	len += sprintf(buffer, "dest_addr  dest_call src_addr   src_call  dev   lci neigh st vs vr va   t  t1  t2  t3  hb    idle Snd-Q Rcv-Q Inode\n");
 
 	for (s = rose_list; s != NULL; s = s->next) {
 		if ((dev = s->protinfo.rose->device) == NULL)
@@ -1372,9 +1510,10 @@
 		else
 			callsign = ax2asc(&s->protinfo.rose->source_call);
 
-		len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X  %d  %d  %d  %d %3d %3d %3d %3d %3d %3d/%03d %5d %5d %ld\n",
+		len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %05d  %d  %d  %d  %d %3d %3d %3d %3d %3d %3d/%03d %5d %5d %ld\n",
 			rose2asc(&s->protinfo.rose->source_addr), callsign,
 			devname,  s->protinfo.rose->lci & 0x0FFF,
+			(s->protinfo.rose->neighbour) ? s->protinfo.rose->neighbour->number : 0,
 			s->protinfo.rose->state,
 			s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va,
 			s->protinfo.rose->timer / ROSE_SLOWHZ,
@@ -1384,8 +1523,7 @@
 			s->protinfo.rose->hb    / ROSE_SLOWHZ,
 			0, 0,
 			s->wmem_alloc, s->rmem_alloc,
-			s->socket && SOCK_INODE(s->socket) ?
-			SOCK_INODE(s->socket)->i_ino : 0);
+			s->socket && SOCK_INODE(s->socket) ? SOCK_INODE(s->socket)->i_ino : 0);
 
 		pos = begin + len;
 
@@ -1468,7 +1606,11 @@
 	{"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
 	{"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
 	{"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
-	{"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
+	{"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose6", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose8", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+	{"rose9", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
 };
 
 void rose_proto_init(struct net_proto *pro)
@@ -1479,18 +1621,20 @@
 
 	sock_register(rose_proto_ops.family, &rose_proto_ops);
 	register_netdevice_notifier(&rose_dev_notifier);
-	printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.3 for AX25.035 Linux 2.0\n");
+	printk(KERN_INFO "F6FBB/G4KLX ROSE for Linux. Version 0.63 for AX25.035 Linux 2.0\n");
 
 	if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame))
 		printk(KERN_ERR "ROSE: unable to register protocol with AX.25\n");
 	if (!ax25_linkfail_register(rose_link_failed))
 		printk(KERN_ERR "ROSE: unable to register linkfail handler with AX.25\n");
 
-	for (i = 0; i < 6; i++)
+	for (i = 0; i < 10; i++)
 		register_netdev(&dev_rose[i]);
 
 	rose_register_sysctl();
 
+	rose_loopback_init();
+
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_rose);
 	proc_net_register(&proc_net_rose_neigh);
@@ -1520,6 +1664,8 @@
 	proc_net_unregister(PROC_NET_RS_NODES);
 	proc_net_unregister(PROC_NET_RS_ROUTES);
 #endif
+	rose_loopback_clear();
+
 	rose_rt_free();
 
 	ax25_protocol_release(AX25_P_ROSE);
@@ -1534,7 +1680,7 @@
 
 	sock_unregister(AF_ROSE);
 
-	for (i = 0; i < 6; i++) {
+	for (i = 0; i < 10; i++) {
 		if (dev_rose[i].priv != NULL) {
 			kfree(dev_rose[i].priv);
 			dev_rose[i].priv = NULL;
diff -u --recursive --new-file v2.0.35/linux/net/rose/changes.doc linux/net/rose/changes.doc
--- v2.0.35/linux/net/rose/changes.doc	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/changes.doc	Sun Nov 15 10:33:22 1998
@@ -0,0 +1,10 @@
+Changes information in rose driver by F6FBB
+
+980728 : Added link add and route add from remote application
+         An adjacent is not removed if no node. It points to the
+		 dummy node (0000000000)
+		 
+980728 : Routing algorithm was falling to the next route if no choice
+         available for the selected address. Corrected.
+		 
+980728 : Connect did not report cause and diagnostic. Added.
diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_in.c linux/net/rose/rose_in.c
--- v2.0.35/linux/net/rose/rose_in.c	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/rose_in.c	Sun Nov 15 10:33:22 1998
@@ -17,7 +17,6 @@
  *
  *	History
  *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_in.c
- *	ROSE 003	Jonathan(G4KLX)	Removed M bit processing.
  */
 
 #include <linux/config.h>
@@ -45,6 +44,45 @@
 #include <linux/interrupt.h>
 #include <net/rose.h>
 
+static int rose_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
+{
+	struct sk_buff *skbo, *skbn = skb;
+
+	if (more) {
+		sk->protinfo.rose->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.rose->frag_queue, skb);
+		return 0;
+	}
+	
+	if (!more && sk->protinfo.rose->fraglen > 0) {	/* End of fragment */
+		sk->protinfo.rose->fraglen += skb->len;
+		skb_queue_tail(&sk->protinfo.rose->frag_queue, skb);
+
+		if ((skbn = alloc_skb(sk->protinfo.rose->fraglen, GFP_ATOMIC)) == NULL)
+			return 1;
+
+		skbn->free = 1;
+		skbn->arp  = 1;
+		skbn->sk   = sk;
+		skbn->h.raw = skbn->data;
+
+		skbo = skb_dequeue(&sk->protinfo.rose->frag_queue);
+		memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+		kfree_skb(skbo, FREE_READ);
+
+		while ((skbo = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL) {
+			skb_pull(skbo, ROSE_MIN_LEN);
+			memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+			kfree_skb(skbo, FREE_READ);
+		}
+
+		sk->protinfo.rose->fraglen = 0;		
+	}
+
+	/* printk("FBB : lg=%ld\n", skbn->len); */
+	return sock_queue_rcv_skb(sk, skbn);
+}
+
 /*
  * State machine for state 1, Awaiting Call Accepted State.
  * The handling of the timer(s) is in file rose_timer.c.
@@ -52,6 +90,8 @@
  */
 static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
 {
+	int len;
+	
 	switch (frametype) {
 
 		case ROSE_CALL_ACCEPTED:
@@ -80,6 +120,18 @@
 			if (!sk->dead)
 				sk->state_change(sk);
 			sk->dead                 = 1;
+			len = 5;	/* Minimum size of the frame data */
+			if (skb->len > len) {
+				/* Address block */
+				len += 1;
+				len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2;
+				len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2;
+
+				if (skb->len > len) {
+					/* Facilities */
+					rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities);
+				}
+			}
 			break;
 
 		default:
@@ -96,12 +148,27 @@
  */
 static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype)
 {
+	int len;
+	
 	switch (frametype) {
 
 		case ROSE_CLEAR_REQUEST:
 			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 			sk->protinfo.rose->cause      = skb->data[3];
 			sk->protinfo.rose->diagnostic = skb->data[4];
+			len = 5;
+			if (skb->len > len) {
+				/* Address block */
+				len += 1;
+				len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2;
+				len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2;
+
+				if (skb->len > len) {
+					/* Facilities */
+					rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities);
+				}
+			}
+			/* fall in next case ... */
 		case ROSE_CLEAR_CONFIRMATION:
 			rose_clear_queues(sk);
 			sk->protinfo.rose->neighbour->use--;
@@ -129,6 +196,7 @@
 static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m)
 {
 	int queued = 0;
+	int len;
 
 	switch (frametype) {
 
@@ -155,14 +223,22 @@
 			if (!sk->dead)
 				sk->state_change(sk);
 			sk->dead                 = 1;
+			len = 5;
+			if (skb->len > len) {
+				/* Address block */
+				len += 1;
+				len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2;
+				len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2;
+
+				if (skb->len > len) {
+					/* Facilities */
+					rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities);
+				}
+			}
 			break;
 
 		case ROSE_RR:
 		case ROSE_RNR:
-			if (frametype == ROSE_RNR)
-				sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY;
-			else
-				sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
 			if (!rose_validate_nr(sk, nr)) {
 				rose_clear_queues(sk);
 				rose_write_internal(sk, ROSE_RESET_REQUEST);
@@ -174,10 +250,12 @@
 				sk->protinfo.rose->state     = ROSE_STATE_4;
 				sk->protinfo.rose->timer     = sk->protinfo.rose->t2;
 			} else {
-				if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
-					sk->protinfo.rose->va = nr;
-				} else {
-					rose_check_iframes_acked(sk, nr);
+				rose_frames_acked(sk, nr);
+				/* F6FBB : only set the flag ! */
+				if (frametype == ROSE_RNR)
+					sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY;
+				else {
+					sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
 				}
 			}
 			break;
@@ -196,18 +274,26 @@
 				sk->protinfo.rose->timer     = sk->protinfo.rose->t2;
 				break;
 			}
-			if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
-				sk->protinfo.rose->va = nr;
-			} else {
-				rose_check_iframes_acked(sk, nr);
-			}
-			if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)
-				break;
+			rose_frames_acked(sk, nr);
 			if (ns == sk->protinfo.rose->vr) {
-				if (sock_queue_rcv_skb(sk, skb) == 0) {
+				if (rose_queue_rx_frame(sk, skb, m) == 0) {
 					sk->protinfo.rose->vr = (sk->protinfo.rose->vr + 1) % ROSE_MODULUS;
 					queued = 1;
 				} else {
+					/* should never happen ! */
+					rose_clear_queues(sk);
+					rose_write_internal(sk, ROSE_RESET_REQUEST);
+					sk->protinfo.rose->condition = 0x00;
+					sk->protinfo.rose->vs        = 0;
+					sk->protinfo.rose->vr        = 0;
+					sk->protinfo.rose->va        = 0;
+					sk->protinfo.rose->vl        = 0;
+					sk->protinfo.rose->state     = ROSE_STATE_4;
+					sk->protinfo.rose->timer     = sk->protinfo.rose->t2;
+					break;
+				}
+				/* F6FBB : check if room enough for a full window */
+				if (sk->rmem_alloc > (sk->rcvbuf - ROSE_MAX_WINDOW_LEN)) {
 					sk->protinfo.rose->condition |= ROSE_COND_OWN_RX_BUSY;
 				}
 			}
@@ -240,6 +326,8 @@
  */
 static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype)
 {
+	int len;
+	
 	switch (frametype) {
 
 		case ROSE_RESET_REQUEST:
@@ -268,6 +356,18 @@
 			if (!sk->dead)
 				sk->state_change(sk);
 			sk->dead                 = 1;
+			len = 5;
+			if (skb->len > len) {
+				/* Address block */
+				len += 1;
+				len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2;
+				len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2;
+
+				if (skb->len > len) {
+					/* Facilities */
+					rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities);
+				}
+			}
 			break;
 
 		default:
@@ -284,6 +384,8 @@
  */
 static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype)
 {
+	int len;
+	
 	switch (frametype) {
 
 		case ROSE_CLEAR_REQUEST:
@@ -299,6 +401,18 @@
 			if (!sk->dead)
 				sk->state_change(sk);
 			sk->dead                 = 1;
+			len = 5;
+			if (skb->len > len) {
+				/* Address block */
+				len += 1;
+				len += (((skb->data[5] >> 4) & 0x0F) + 1) / 2;
+				len += (((skb->data[5] >> 0) & 0x0F) + 1) / 2;
+
+				if (skb->len > len) {
+					/* Facilities */
+					rose_parse_facilities(skb->data + len, &sk->protinfo.rose->facilities);
+				}
+			}
 			break;
 	}
 
diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_link.c linux/net/rose/rose_link.c
--- v2.0.35/linux/net/rose/rose_link.c	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/rose_link.c	Sun Nov 15 10:33:22 1998
@@ -101,7 +101,7 @@
 	else
 		rose_call = &rose_callsign;
 
-	neigh->ax25 = ax25_send_frame(skb, 0, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+	neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
 
 	return (neigh->ax25 != NULL);
 }
@@ -134,6 +134,8 @@
 
 	switch (frametype) {
 		case ROSE_RESTART_REQUEST:
+			/* Stop all existing routes on this link - F6FBB */
+			rose_clean_neighbour(neigh);
 			neigh->t0timer   = 0;
 			neigh->restarted = 1;
 			neigh->dce_mode  = (skb->data[3] == ROSE_DTE_ORIGINATED);
@@ -261,10 +263,17 @@
 {
 	struct sk_buff *skb;
 	unsigned char *dptr;
+	struct device *first;
 	int len;
 
 	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
-
+	
+	first = rose_dev_first();
+	if (first) {	
+		/* F6FBB - Adding facilities */
+		len += 6 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN;
+	}
+	
 	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
 		return;
 
@@ -272,7 +281,7 @@
 
 	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
 
-	dptr = skb_put(skb, ROSE_MIN_LEN + 3);
+	dptr = skb_put(skb, skb_tailroom(skb));
 
 	*dptr++ = AX25_P_ROSE;
 	*dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI;
@@ -281,18 +290,47 @@
 	*dptr++ = cause;
 	*dptr++ = diagnostic;
 
+	if (first) {	
+		/* F6FBB - Adding facilities */
+		*dptr++ = 0x00;		/* Address length */
+		*dptr++ = 4 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; /* Facilities length */
+		*dptr++ = 0;
+		*dptr++ = FAC_NATIONAL;
+		*dptr++ = FAC_NATIONAL_FAIL_CALL;
+		*dptr++ = AX25_ADDR_LEN;
+		memcpy(dptr, &rose_callsign, AX25_ADDR_LEN);
+		dptr += AX25_ADDR_LEN;
+		*dptr++ = FAC_NATIONAL_FAIL_ADD;
+		*dptr++ = ROSE_ADDR_LEN + 1;
+		*dptr++ = ROSE_ADDR_LEN * 2;
+		memcpy(dptr, first->dev_addr, ROSE_ADDR_LEN);
+	}
+	
 	if (!rose_send_frame(skb, neigh))
 		kfree_skb(skb, FREE_WRITE);
 }
 
 void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
 {
+	rose_address *dest_addr;
 	unsigned char *dptr;
 
 #ifdef CONFIG_FIREWALL
 	if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
 		return;
 #endif
+
+	/*
+	 * Check to see if its for us, if it is put it onto the loopback
+	 * queue.
+	 */
+        dest_addr = (rose_address *)(skb->data + 4);
+  
+	if ((neigh->dce_mode == -1) || (rose_dev_get(dest_addr) != NULL)) {
+		neigh->dce_mode = -1;
+		rose_loopback_queue(skb, neigh);
+		return;
+	}
 
 	if (!rose_link_up(neigh))
 		neigh->restarted = 0;
diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_loopback.c linux/net/rose/rose_loopback.c
--- v2.0.35/linux/net/rose/rose_loopback.c	Wed Dec 31 16:00:00 1969
+++ linux/net/rose/rose_loopback.c	Sun Nov 15 10:33:22 1998
@@ -0,0 +1,135 @@
+/*
+ *	ROSE release 003
+ *
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	ROSE 003	Jonathan(G4KLX)	Created this file from nr_loopback.c.
+ *
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/timer.h>
+#include <net/ax25.h>
+#include <linux/skbuff.h>
+#include <net/rose.h>
+
+static struct sk_buff_head loopback_queue;
+static struct timer_list loopback_timer;
+
+static void rose_set_loopback_timer(void);
+
+void rose_loopback_init(void)
+{
+	skb_queue_head_init(&loopback_queue);
+
+	init_timer(&loopback_timer);
+}
+
+void rose_loopback_clear(void)
+{
+	struct sk_buff *skb;
+
+	del_timer(&loopback_timer);
+
+	while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_READ);
+	}
+}
+
+static int rose_loopback_running(void)
+{
+	return (loopback_timer.prev != NULL || loopback_timer.next != NULL);
+}
+
+int rose_loopback_queue(struct sk_buff *skb, struct rose_neigh *neigh)
+{
+	struct sk_buff *skbn;
+
+	skbn = skb_clone(skb, GFP_ATOMIC);
+
+	kfree_skb(skb, FREE_WRITE);
+
+	if (skbn != NULL) {
+		skbn->sk = (struct sock *)neigh;
+		skb_queue_tail(&loopback_queue, skbn);
+	}
+
+	if (!rose_loopback_running())
+	{
+		rose_set_loopback_timer();
+	}
+	return 1;
+}
+
+static void rose_loopback_timer(unsigned long);
+
+static void rose_set_loopback_timer(void)
+{
+	del_timer(&loopback_timer);
+
+	loopback_timer.data     = 0;
+	loopback_timer.function = &rose_loopback_timer;
+	loopback_timer.expires  = jiffies + 10;
+
+	add_timer(&loopback_timer);
+}
+
+static void rose_loopback_timer(unsigned long param)
+{
+	struct sk_buff *skb;
+	struct rose_neigh *rose_neigh;
+	struct sock *sk;
+	struct device *dev;
+	rose_address *dest_addr;
+	unsigned int lci;
+	unsigned short frametype;
+
+	while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
+		rose_neigh = (struct rose_neigh *)skb->sk;
+		lci        = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
+		dest_addr = (rose_address *)(skb->data + 4);
+
+		/* F1OAT : Patch the LCI for proper loopback operation */
+		/* Work only if the system open less than 2048 VC !!! */
+		
+		lci = 4096 - lci;
+		skb->data[0] = (lci >> 8) & 0x0F;
+		skb->data[1] = lci & 0xFF;
+		
+		skb->sk = NULL;
+
+		frametype = skb->data[2];
+
+		if (frametype == ROSE_CALL_REQUEST) {
+			if ((dev = rose_dev_get(dest_addr)) == NULL) {
+				kfree_skb(skb, FREE_READ);
+				continue;			
+			}
+			if (!rose_rx_call_request(skb, dev, rose_neigh, lci))
+				kfree_skb(skb, FREE_READ); 
+		}
+		else {
+			if ((sk = rose_find_socket(lci, rose_neigh)) == NULL) {
+				kfree_skb(skb, FREE_READ);
+				continue;
+			}
+			skb->h.raw = skb->data;
+
+			if (rose_process_rx_frame(sk, skb) == 0)
+				kfree_skb(skb, FREE_READ); 
+		}
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_out.c linux/net/rose/rose_out.c
--- v2.0.35/linux/net/rose/rose_out.c	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/rose_out.c	Sun Nov 15 10:33:22 1998
@@ -11,7 +11,6 @@
  *
  *	History
  *	ROSE 001	Jonathan(G4KLX)	Cloned from nr_out.c
- *	ROSE 003	Jonathan(G4KLX)	Removed M bit processing.
  */
 
 #include <linux/config.h>
@@ -55,39 +54,56 @@
 
 void rose_kick(struct sock *sk)
 {
-	struct sk_buff *skb;
-	unsigned short end;
+	struct sk_buff *skb, *skbn;
+	unsigned short start, end;
 
-	del_timer(&sk->timer);
+	if (sk->protinfo.rose->state != ROSE_STATE_3)
+		return;
 
-	end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS;
+	if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY)
+		return;
 
-	if (!(sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) &&
-	    sk->protinfo.rose->vs != end                             &&
-	    skb_peek(&sk->write_queue) != NULL) {
-		/*
-		 * Transmit data until either we're out of data to send or
-		 * the window is full.
-		 */
+	if (skb_peek(&sk->write_queue) == NULL)
+		return;
 
-		skb  = skb_dequeue(&sk->write_queue);
+	start = (skb_peek(&sk->protinfo.rose->ack_queue) == NULL) ? sk->protinfo.rose->va : sk->protinfo.rose->vs;
+	end   = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS;
 
-		do {
-			/*
-			 * Transmit the frame.
-			 */
-			rose_send_iframe(sk, skb);
+	if (start == end)
+		return;
 
-			sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
+	sk->protinfo.rose->vs = start;
 
-		} while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+	/*
+	 * Transmit data until either we're out of data to send or
+	 * the window is full.
+	 */
+
+	skb  = skb_dequeue(&sk->write_queue);
+
+	do {
+		if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+			skb_queue_head(&sk->write_queue, skb);
+			break;
+		}
 
-		sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
-		sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
-		sk->protinfo.rose->timer      = 0;
-	}
+		/*
+		 * Transmit the frame copy.
+		 */
+		rose_send_iframe(sk, skbn);
 
-	rose_set_timer(sk);
+		sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
+
+		/*
+		 * Requeue the original data frame.
+		 */
+		skb_queue_tail(&sk->protinfo.rose->ack_queue, skb);
+
+	} while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+
+	sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
+	sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+	sk->protinfo.rose->timer      = 0;
 }
 
 /*
@@ -105,16 +121,6 @@
 	sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
 	sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
 	sk->protinfo.rose->timer      = 0;
-}
-
-void rose_check_iframes_acked(struct sock *sk, unsigned short nr)
-{
-	if (sk->protinfo.rose->vs == nr) {
-		sk->protinfo.rose->va = nr;
-	} else {
-		if (sk->protinfo.rose->va != nr)
-			sk->protinfo.rose->va = nr;
-	}
 }
 
 #endif
diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.0.35/linux/net/rose/rose_route.c	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/rose_route.c	Sun Nov 15 10:33:22 1998
@@ -54,6 +54,17 @@
 static struct rose_route *rose_route_list = NULL;
 
 static void rose_remove_neigh(struct rose_neigh *);
+static int rose_del_node(struct rose_route_struct *, struct device *);
+
+static int rose_is_null(rose_address *address)
+{
+	int i;
+	
+	for (i = 0 ; i < sizeof(rose_address) ; i++)
+		if (address->rose_addr[i])
+			return 0;
+	return 1;
+}
 
 /*
  *	Add a new route to a node, and in the process add the node and the
@@ -63,6 +74,7 @@
 {
 	struct rose_node  *rose_node, *rose_tmpn, *rose_tmpp;
 	struct rose_neigh *rose_neigh;
+	struct rose_route_struct dummy_route;
 	unsigned long flags;
 	int i;
 
@@ -166,16 +178,22 @@
 
 		rose_neigh->count++;
 
-		return 0;
-	}
-
-	/* We have space, slot it in */
-	if (rose_node->count < 3) {
+	} else if (rose_node->count < ROSE_MAX_ALTERNATE) {
+		/* We have space, slot it in */
 		rose_node->neighbour[rose_node->count] = rose_neigh;
 		rose_node->count++;
 		rose_neigh->count++;
 	}
 
+	if (!rose_is_null(&rose_route->address))
+	{
+		/* Delete this neighbourg from the dummy node 0000000000 */
+		dummy_route = *rose_route;
+		dummy_route.mask = 10;
+		memset(&dummy_route.address, 0, sizeof(rose_address));
+		rose_del_node(&dummy_route, dev);
+	}
+	
 	return 0;
 }
 
@@ -252,11 +270,11 @@
 	struct rose_route *s;
 	unsigned long flags;
 
-	if (rose_route->neigh1 != NULL)
-		rose_route->neigh1->use--;
+	if (rose_route->tr1.neigh != NULL)
+		rose_route->tr1.neigh->use--;
 
-	if (rose_route->neigh2 != NULL)
-		rose_route->neigh2->use--;
+	if (rose_route->tr2.neigh != NULL)
+		rose_route->tr2.neigh->use--;
 
 	save_flags(flags);
 	cli();
@@ -290,6 +308,7 @@
 {
 	struct rose_node  *rose_node;
 	struct rose_neigh *rose_neigh;
+	struct rose_route_struct dummy_route;
 	int i;
 
 	for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
@@ -308,8 +327,16 @@
 		if (rose_node->neighbour[i] == rose_neigh) {
 			rose_neigh->count--;
 
-			if (rose_neigh->count == 0 && rose_neigh->use == 0)
+			if (rose_neigh->count == 0) {
+				/* Add a dummy node to keep the neighbourg still visible */
+				dummy_route = *rose_route;
+				dummy_route.mask = 10;
+				memset(&dummy_route.address, 0, sizeof(rose_address));
+				rose_add_node(&dummy_route, dev);
+			}
+			/* if (rose_neigh->count == 0 && rose_neigh->use == 0) {
 				rose_remove_neigh(rose_neigh);
+			} */
 
 			rose_node->count--;
 
@@ -388,7 +415,7 @@
 		s          = rose_route;
 		rose_route = rose_route->next;
 
-		if (s->neigh1->dev == dev || s->neigh2->dev == dev)
+		if (s->tr1.neigh->dev == dev || s->tr2.neigh->dev == dev)
 			rose_remove_route(s);
 	}
 }
@@ -472,8 +499,8 @@
 	struct rose_route *rose_route;
 
 	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next)
-		if ((rose_route->neigh1 == neigh && rose_route->lci1 == lci) ||
-		    (rose_route->neigh2 == neigh && rose_route->lci2 == lci))
+		if ((rose_route->tr1.neigh == neigh && rose_route->tr1.lci == lci) ||
+		    (rose_route->tr2.neigh == neigh && rose_route->tr2.lci == lci))
 			return rose_route;
 
 	return NULL;
@@ -496,7 +523,7 @@
 				else
 					failed = 1;
 			}
-			/* F6FBB : All nodes for this route are out of order */
+			/* do not accept other node address */
 			break;
 		}
 	}
@@ -529,8 +556,8 @@
 			memcpy_fromfs(&rose_route, arg, sizeof(struct rose_route_struct));
 			if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
 				return -EINVAL;
-			if (rose_dev_get(&rose_route.address) != NULL)	/* Can't add routes to ourself */
-				return -EINVAL;
+			/* if (rose_dev_get(&rose_route.address) != NULL)	/ Can't add routes to ourself /
+				return -EINVAL; */
 			if (rose_route.mask > 10) /* Mask can't be more than 10 digits */
 				return -EINVAL;
 
@@ -571,25 +598,25 @@
 	rose_route = rose_route_list;
 
 	while (rose_route != NULL) {
-		if ((rose_route->neigh1 == rose_neigh && rose_route->neigh2 == rose_neigh) ||
-		    (rose_route->neigh1 == rose_neigh && rose_route->neigh2 == NULL)       ||
-		    (rose_route->neigh2 == rose_neigh && rose_route->neigh1 == NULL)) {
+		if ((rose_route->tr1.neigh == rose_neigh && rose_route->tr2.neigh == rose_neigh) ||
+		    (rose_route->tr1.neigh == rose_neigh && rose_route->tr2.neigh == NULL)       ||
+		    (rose_route->tr2.neigh == rose_neigh && rose_route->tr1.neigh == NULL)) {
 			s = rose_route->next;
 			rose_remove_route(rose_route);
 			rose_route = s;
 			continue;
 		}
 
-		if (rose_route->neigh1 == rose_neigh) {
-			rose_route->neigh1->use--;
-			rose_route->neigh1 = NULL;
-			rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0);
+		if (rose_route->tr1.neigh == rose_neigh) {
+			rose_route->tr1.neigh->use--;
+			rose_route->tr1.neigh = NULL;
+			rose_transmit_clear_request(rose_route->tr2.neigh, rose_route->tr2.lci, ROSE_OUT_OF_ORDER, 0);
 		}
 
-		if (rose_route->neigh2 == rose_neigh) {
-			rose_route->neigh2->use--;
-			rose_route->neigh2 = NULL;
-			rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0);
+		if (rose_route->tr2.neigh == rose_neigh) {
+			rose_route->tr2.neigh->use--;
+			rose_route->tr2.neigh = NULL;
+			rose_transmit_clear_request(rose_route->tr1.neigh, rose_route->tr1.lci, ROSE_OUT_OF_ORDER, 0);
 		}
 
 		rose_route = rose_route->next;
@@ -634,13 +661,23 @@
 }
 
 /*
+ *	A Restart has occured. Delete all existing routes to this neighbour
+ */
+void rose_clean_neighbour(struct rose_neigh *rose_neigh)
+{
+	rose_del_route_by_neigh(rose_neigh);
+	rose_kill_by_neigh(rose_neigh);
+}
+
+
+/*
  *	Route a frame to an appropriate AX.25 connection.
  */
 int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 {
 	struct rose_neigh *rose_neigh, *new_neigh;
 	struct rose_route *rose_route;
-	struct rose_facilities facilities;
+	struct rose_facilities_struct facilities;
 	rose_address *src_addr, *dest_addr;
 	struct sock *sk;
 	unsigned short frametype;
@@ -648,6 +685,8 @@
 	unsigned char cause, diagnostic;
 	struct device *dev;
 	unsigned long flags;
+	int len;
+	struct sk_buff *skbn;
 
 #ifdef CONFIG_FIREWALL
 	if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
@@ -663,8 +702,10 @@
 		if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->device == rose_neigh->dev)
 			break;
 
-	if (rose_neigh == NULL)
+	if (rose_neigh == NULL) {
+		printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr));
 		return 0;
+	}
 
 	/*
 	 *	Obviously the link is working, halt the ftimer.
@@ -684,16 +725,36 @@
 	 *	Find an existing socket.
 	 */
 	if ((sk = rose_find_socket(lci, rose_neigh)) != NULL) {
-		skb->h.raw = skb->data;
-		return rose_process_rx_frame(sk, skb);
+		if (frametype == ROSE_CALL_REQUEST)
+		{
+			/* F6FBB - Remove an existing unused socket */
+			rose_clear_queues(sk);
+			sk->protinfo.rose->cause      = ROSE_NETWORK_CONGESTION;
+			sk->protinfo.rose->diagnostic = 0;
+			sk->protinfo.rose->neighbour->use--;
+			sk->protinfo.rose->neighbour = NULL;
+			sk->protinfo.rose->state = ROSE_STATE_0;
+			sk->state                = TCP_CLOSE;
+			sk->err                  = 0;
+			sk->shutdown            |= SEND_SHUTDOWN;
+			if (!sk->dead)
+				sk->state_change(sk);
+			sk->dead                 = 1;
+		}
+		else
+		{
+			skb->h.raw = skb->data;
+			return rose_process_rx_frame(sk, skb);
+		}
 	}
 
 	/*
-	 *	Is is a Call Request and is it for us ?
+	 *	Is it a Call Request and is it for us ?
 	 */
 	if (frametype == ROSE_CALL_REQUEST)
-		if ((dev = rose_dev_get(dest_addr)) != NULL)
+		if ((dev = rose_dev_get(dest_addr)) != NULL) {
 			return rose_rx_call_request(skb, dev, rose_neigh, lci);
+		}
 
 	if (!sysctl_rose_routing_control) {
 		rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0);
@@ -704,12 +765,20 @@
 	 *	Route it to the next in line if we have an entry for it.
 	 */
 	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
-		if (rose_route->lci1 == lci && rose_route->neigh1 == rose_neigh) {
-			if (rose_route->neigh2 != NULL) {
+		if (rose_route->tr1.lci == lci && rose_route->tr1.neigh == rose_neigh) {
+			if (frametype == ROSE_CALL_REQUEST) {
+				/* F6FBB - Remove an existing unused route */
+				rose_remove_route(rose_route);
+				break;
+			} else if (rose_route->tr2.neigh != NULL) {
+				if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+					kfree_skb(skb, FREE_READ);
+					skb = skbn;
+				}
 				skb->data[0] &= 0xF0;
-				skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
-				skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
-				rose_transmit_link(skb, rose_route->neigh2);
+				skb->data[0] |= (rose_route->tr2.lci >> 8) & 0x0F;
+				skb->data[1]  = (rose_route->tr2.lci >> 0) & 0xFF;
+				rose_transmit_link(skb, rose_route->tr2.neigh);
 				if (frametype == ROSE_CLEAR_CONFIRMATION)
 					rose_remove_route(rose_route);
 				return 1;
@@ -719,12 +788,20 @@
 				return 0;
 			}
 		}
-		if (rose_route->lci2 == lci && rose_route->neigh2 == rose_neigh) {
-			if (rose_route->neigh1 != NULL) {
+		if (rose_route->tr2.lci == lci && rose_route->tr2.neigh == rose_neigh) {
+			if (frametype == ROSE_CALL_REQUEST) {
+				/* F6FBB - Remove an existing unused route */
+				rose_remove_route(rose_route);
+				break;
+			} else if (rose_route->tr1.neigh != NULL) {
+				if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+					kfree_skb(skb, FREE_READ);
+					skb = skbn;
+				}
 				skb->data[0] &= 0xF0;
-				skb->data[0] |= (rose_route->lci1 >> 8) & 0x0F;
-				skb->data[1]  = (rose_route->lci1 >> 0) & 0xFF;
-				rose_transmit_link(skb, rose_route->neigh1);
+				skb->data[0] |= (rose_route->tr1.lci >> 8) & 0x0F;
+				skb->data[1]  = (rose_route->tr1.lci >> 0) & 0xFF;
+				rose_transmit_link(skb, rose_route->tr1.neigh);
 				if (frametype == ROSE_CLEAR_CONFIRMATION)
 					rose_remove_route(rose_route);
 				return 1;
@@ -741,10 +818,17 @@
 	 *	1. The frame isn't for us,
 	 *	2. It isn't "owned" by any existing route.
 	 */
-	if (frametype != ROSE_CALL_REQUEST)	/* XXX */
+	if (frametype != ROSE_CALL_REQUEST)	{ /* Trashed packet */
 		return 0;
+	}
 
-	if (!rose_parse_facilities(skb, &facilities)) {
+	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
+	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
+
+	memset(&facilities, 0x00, sizeof(struct rose_facilities_struct));
+	
+	if (!rose_parse_facilities(skb->data + len + 4, &facilities)) {
+		printk("CR : rose_parse_facilities error\n");
 		rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
 		return 0;
 	}
@@ -757,16 +841,12 @@
 		    rosecmp(src_addr, &rose_route->src_addr) == 0 &&
 		    ax25cmp(&facilities.dest_call, &rose_route->src_call) == 0 &&
 		    ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) {
-			printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr));
-			printk(KERN_DEBUG "ROSE:                to %s\n", rose2asc(dest_addr));
 			rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120);
 			return 0;
 		}
 	}
 
 	if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) {
-		if (cause == ROSE_NOT_OBTAINABLE)
-			printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr));
 		rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic);
 		return 0;
 	}
@@ -777,33 +857,38 @@
 	}
 
 	if ((rose_route = (struct rose_route *)kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
-		rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120);
+		rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 121);
 		return 0;
 	}
 
-	rose_route->lci1      = lci;
 	rose_route->src_addr  = *src_addr;
 	rose_route->dest_addr = *dest_addr;
 	rose_route->src_call  = facilities.dest_call;
 	rose_route->dest_call = facilities.source_call;
 	rose_route->rand      = facilities.rand;
-	rose_route->neigh1    = rose_neigh;
-	rose_route->lci2      = new_lci;
-	rose_route->neigh2    = new_neigh;
+	rose_route->tr1.lci   = lci;
+	rose_route->tr1.neigh = rose_neigh;
+	rose_route->tr2.lci   = new_lci;
+	rose_route->tr2.neigh = new_neigh;
 
-	rose_route->neigh1->use++;
-	rose_route->neigh2->use++;
+	rose_route->tr1.neigh->use++;
+	rose_route->tr2.neigh->use++;
 
 	save_flags(flags); cli();
 	rose_route->next = rose_route_list;
 	rose_route_list  = rose_route;
 	restore_flags(flags);
 
+	if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+		kfree_skb(skb, FREE_READ);
+		skb = skbn;
+	}
+
 	skb->data[0] &= 0xF0;
-	skb->data[0] |= (rose_route->lci2 >> 8) & 0x0F;
-	skb->data[1]  = (rose_route->lci2 >> 0) & 0xFF;
+	skb->data[0] |= (rose_route->tr2.lci >> 8) & 0x0F;
+	skb->data[1]  = (rose_route->tr2.lci >> 0) & 0xFF;
 
-	rose_transmit_link(skb, rose_route->neigh2);
+	rose_transmit_link(skb, rose_route->tr2.neigh);
 
 	return 1;
 }
@@ -920,22 +1005,22 @@
 	len += sprintf(buffer, "lci  address     callsign   neigh  <-> lci  address     callsign   neigh\n");
 
 	for (rose_route = rose_route_list; rose_route != NULL; rose_route = rose_route->next) {
-		if (rose_route->neigh1 != NULL) {
+		if (rose_route->tr1.neigh != NULL) {
 			len += sprintf(buffer + len, "%3.3X  %-10s  %-9s  %05d      ",
-				rose_route->lci1,
+				rose_route->tr1.lci,
 				rose2asc(&rose_route->src_addr),
 				ax2asc(&rose_route->src_call),
-				rose_route->neigh1->number);
+				rose_route->tr1.neigh->number);
 		} else {
 			len += sprintf(buffer + len, "000  *           *          00000      ");
 		}
 
-		if (rose_route->neigh2 != NULL) {
+		if (rose_route->tr2.neigh != NULL) {
 			len += sprintf(buffer + len, "%3.3X  %-10s  %-9s  %05d\n",
-				rose_route->lci2,
+				rose_route->tr2.lci,
 				rose2asc(&rose_route->dest_addr),
 				ax2asc(&rose_route->dest_call),
-				rose_route->neigh2->number);
+				rose_route->tr2.neigh->number);
 		} else {
 			len += sprintf(buffer + len, "000  *           *          00000\n");
 		}
diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- v2.0.35/linux/net/rose/rose_subr.c	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/rose_subr.c	Sun Nov 15 10:33:22 1998
@@ -46,6 +46,36 @@
 
 	while ((skb = skb_dequeue(&sk->write_queue)) != NULL)
 		kfree_skb(skb, FREE_WRITE);
+
+	while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL)
+		kfree_skb(skb, FREE_WRITE);
+
+#ifdef M_BIT
+	while ((skb = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL)
+		kfree_skb(skb, FREE_READ);
+#endif
+}
+
+/*
+ * This routine purges the input queue of those frames that have been
+ * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
+ * SDL diagram.
+ */
+void rose_frames_acked(struct sock *sk, unsigned short nr)
+{
+	struct sk_buff *skb;
+
+	/*
+	 * Remove all the ack-ed frames from the ack queue.
+	 */
+	if (sk->protinfo.rose->va != nr) {
+
+		while (skb_peek(&sk->protinfo.rose->ack_queue) != NULL && sk->protinfo.rose->va != nr) {
+			skb = skb_dequeue(&sk->protinfo.rose->ack_queue);
+			kfree_skb(skb, FREE_WRITE);
+			sk->protinfo.rose->va = (sk->protinfo.rose->va + 1) % ROSE_MODULUS;
+		}
+	}
 }
 
 /*
@@ -75,7 +105,7 @@
 	struct sk_buff *skb;
 	unsigned char  *dptr;
 	unsigned char  lci1, lci2;
-	char buffer[100];
+	char buffer[250];
 	int len, faclen = 0;
 
 	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
@@ -83,14 +113,32 @@
 	switch (frametype) {
 		case ROSE_CALL_REQUEST:
 			len   += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN;
+			/* facilities */
 			faclen = rose_create_facilities(buffer, sk->protinfo.rose);
 			len   += faclen;
 			break;
 		case ROSE_CALL_ACCEPTED:
-		case ROSE_CLEAR_REQUEST:
 		case ROSE_RESET_REQUEST:
 			len   += 2;
 			break;
+		case ROSE_CLEAR_REQUEST:
+			len   += 3;
+			/* facilities */
+			faclen = 3 + 2 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN;
+			dptr = buffer;
+			*dptr++ = faclen-1;	/* Facilities length */
+			*dptr++ = 0;
+			*dptr++ = FAC_NATIONAL;
+			*dptr++ = FAC_NATIONAL_FAIL_CALL;
+			*dptr++ = AX25_ADDR_LEN;
+			memcpy(dptr, &rose_callsign, AX25_ADDR_LEN);
+			dptr += AX25_ADDR_LEN;
+			*dptr++ = FAC_NATIONAL_FAIL_ADD;
+			*dptr++ = ROSE_ADDR_LEN + 1;
+			*dptr++ = ROSE_ADDR_LEN * 2;
+			memcpy(dptr, &sk->protinfo.rose->source_addr, ROSE_ADDR_LEN);
+			len   += faclen;
+			break;
 	}
 
 	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
@@ -137,6 +185,9 @@
 			*dptr++ = frametype;
 			*dptr++ = sk->protinfo.rose->cause;
 			*dptr++ = sk->protinfo.rose->diagnostic;
+			*dptr++ = 0x00;		/* Address length */
+			memcpy(dptr, buffer, faclen);
+			dptr   += faclen;
 			break;
 
 		case ROSE_RESET_REQUEST:
@@ -144,7 +195,7 @@
 			*dptr++ = lci2;
 			*dptr++ = frametype;
 			*dptr++ = ROSE_DTE_ORIGINATED;
-			*dptr++ = 0;
+			*dptr++ = 0x00;		/* Address length */
 			break;
 
 		case ROSE_RR:
@@ -209,9 +260,11 @@
 	return ROSE_ILLEGAL;
 }
 
-static int rose_parse_national(unsigned char *p, struct rose_facilities *facilities, int len)
+static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *facilities, int len)
 {
-	unsigned char l, n = 0;
+	unsigned char *pt;
+	unsigned char l, lg, n = 0;
+	int fac_national_digis_received = 0;
 
 	do {
 		switch (*p & 0xC0) {
@@ -238,12 +291,33 @@
 			case 0xC0:
 				l = p[1];
 				if (*p == FAC_NATIONAL_DEST_DIGI) {
-					memcpy(&facilities->source_digi, p + 2, AX25_ADDR_LEN);
-					facilities->source_ndigis = 1;
+					if (!fac_national_digis_received) {
+						memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN);
+						facilities->source_ndigis = 1;
+					}
+				}
+				else if (*p == FAC_NATIONAL_SRC_DIGI) {
+					if (!fac_national_digis_received) {
+						memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN);
+						facilities->dest_ndigis = 1;
+					}
+				}
+				else if (*p == FAC_NATIONAL_FAIL_CALL) {
+					memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN);
+				}
+				else if (*p == FAC_NATIONAL_FAIL_ADD) {
+					memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN);
 				}
-				if (*p == FAC_NATIONAL_SRC_DIGI) {
-					memcpy(&facilities->dest_digi,   p + 2, AX25_ADDR_LEN);
-					facilities->dest_ndigis = 1;
+				else if (*p == FAC_NATIONAL_DIGIS) {
+					fac_national_digis_received = 1;
+					facilities->source_ndigis = 0;
+					facilities->dest_ndigis   = 0;
+					for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) {
+						if (pt[6] & AX25_HBIT)
+							memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN);
+						else
+							memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN);
+					}
 				}
 				p   += l + 2;
 				n   += l + 2;
@@ -255,7 +329,7 @@
 	return n;
 }
 
-static int rose_parse_ccitt(unsigned char *p, struct rose_facilities *facilities, int len)
+static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *facilities, int len)
 {
 	unsigned char l, n = 0;
 	char callsign[11];
@@ -288,7 +362,7 @@
 					callsign[l - 10] = '\0';
 					facilities->source_call = *asc2ax(callsign);
 				}
-				if (*p == FAC_CCITT_SRC_NSAP) {
+				else if (*p == FAC_CCITT_SRC_NSAP) {
 					memcpy(&facilities->dest_addr, p + 7, ROSE_ADDR_LEN);
 					memcpy(callsign, p + 12, l - 10);
 					callsign[l - 10] = '\0';
@@ -304,17 +378,9 @@
 	return n;
 }
 
-int rose_parse_facilities(struct sk_buff *skb, struct rose_facilities *facilities)
+int rose_parse_facilities(unsigned char *p, struct rose_facilities_struct *facilities)
 {
 	int facilities_len, len;
-	unsigned char *p;
-
-	memset(facilities, 0x00, sizeof(struct rose_facilities));
-
-	len  = (((skb->data[3] >> 4) & 0x0F) + 1) / 2;
-	len += (((skb->data[3] >> 0) & 0x0F) + 1) / 2;
-
-	p = skb->data + len + 4;
 
 	facilities_len = *p++;
 
@@ -327,25 +393,25 @@
 			p++;
 
 			switch (*p) {
-				case FAC_NATIONAL:		/* National */
+				case FAC_NATIONAL:		/* National 0x00 */
 					len = rose_parse_national(p + 1, facilities, facilities_len - 1);
 					facilities_len -= len + 1;
 					p += len + 1;
 					break;
 
-				case FAC_CCITT:		/* CCITT */
+				case FAC_CCITT:		/* CCITT 0x0F */
 					len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1);
 					facilities_len -= len + 1;
 					p += len + 1;
 					break;
 
 				default:
-					printk(KERN_DEBUG "rose_parse_facilities: unknown facilities family %02X\n", *p);
 					facilities_len--;
 					p++;
 					break;
 			}
-		}
+		} 
+		else break;	/* Error in facilities format */
 	}
 
 	return 1;
@@ -356,6 +422,7 @@
 	unsigned char *p = buffer + 1;
 	char *callsign;
 	int len;
+	int nb;
 
 	/* National Facilities */
 	if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) {
@@ -368,19 +435,43 @@
 			*p++ = (rose->rand >> 0) & 0xFF;
 		}
 
-		if (rose->source_ndigis == 1) {
+		/* Sent before older facilities */
+		if ((rose->source_ndigis > 0) || (rose->dest_ndigis > 0)) {
+			int maxdigi = 0;
+			*p++ = FAC_NATIONAL_DIGIS;
+			*p++ = AX25_ADDR_LEN * (rose->source_ndigis + rose->dest_ndigis);
+			for (nb = 0 ; nb < rose->source_ndigis ; nb++) {
+				if (++maxdigi >= ROSE_MAX_DIGIS)
+					break;
+				memcpy(p, &rose->source_digis[nb], AX25_ADDR_LEN);
+				p[6] |= AX25_HBIT;
+				p += AX25_ADDR_LEN;
+			}
+			for (nb = 0 ; nb < rose->dest_ndigis ; nb++) {
+				if (++maxdigi >= ROSE_MAX_DIGIS)
+					break;
+				memcpy(p, &rose->dest_digis[nb], AX25_ADDR_LEN);
+				p[6] &= ~AX25_HBIT;
+				p += AX25_ADDR_LEN;
+			}
+		}
+
+		/* For compatibility */
+		if (rose->source_ndigis > 0) {
 			*p++ = FAC_NATIONAL_SRC_DIGI;
 			*p++ = AX25_ADDR_LEN;
-			memcpy(p, &rose->source_digi, AX25_ADDR_LEN);
+			memcpy(p, &rose->source_digis[0], AX25_ADDR_LEN);
 			p   += AX25_ADDR_LEN;
 		}
 
-		if (rose->dest_ndigis == 1) {
+		/* For compatibility */
+		if (rose->dest_ndigis > 0) {
 			*p++ = FAC_NATIONAL_DEST_DIGI;
 			*p++ = AX25_ADDR_LEN;
-			memcpy(p, &rose->dest_digi, AX25_ADDR_LEN);
+			memcpy(p, &rose->dest_digis[0], AX25_ADDR_LEN);
 			p   += AX25_ADDR_LEN;
 		}
+		
 	}
 
 	*p++ = 0x00;
diff -u --recursive --new-file v2.0.35/linux/net/rose/rose_timer.c linux/net/rose/rose_timer.c
--- v2.0.35/linux/net/rose/rose_timer.c	Sun Nov 15 10:50:02 1998
+++ linux/net/rose/rose_timer.c	Sun Nov 15 10:33:23 1998
@@ -66,6 +66,7 @@
 static void rose_timer(unsigned long param)
 {
 	struct sock *sk = (struct sock *)param;
+	struct device *first;
 
 	switch (sk->protinfo.rose->state) {
 		case ROSE_STATE_0:
@@ -82,7 +83,7 @@
 			/*
 			 * Check for the state of the receive buffer.
 			 */
-			if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) {
+			if (sk->rmem_alloc < ((sk->rcvbuf - ROSE_MAX_WINDOW_LEN) / 2) && (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) {
 				sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY;
 				sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
 				sk->protinfo.rose->vl         = sk->protinfo.rose->vr;
@@ -120,11 +121,25 @@
 		case ROSE_STATE_1:	/* T1 */
 		case ROSE_STATE_4:	/* T2 */
 			rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+			/* F6FBB - Disconnect the calling station
 			sk->protinfo.rose->state = ROSE_STATE_2;
 			sk->protinfo.rose->timer = sk->protinfo.rose->t3;
-			break;
+			break; */
+			
+			/* Falls to next case */
 
 		case ROSE_STATE_2:	/* T3 */
+			/* F6FBB - Added Cause and diagnostic */
+			sk->protinfo.rose->cause = ROSE_DTE_ORIGINATED;
+			sk->protinfo.rose->diagnostic = 0x30;
+			
+			/* F6FBB - Added Facilities */
+			first = rose_dev_first();
+			if (first) {	
+				sk->protinfo.rose->facilities.fail_call = rose_callsign;
+				memcpy(&sk->protinfo.rose->facilities.fail_addr, first->dev_addr, ROSE_ADDR_LEN);
+			}
+	
 			rose_clear_queues(sk);
 			sk->protinfo.rose->neighbour->use--;
 			sk->protinfo.rose->state = ROSE_STATE_0;
diff -u --recursive --new-file v2.0.35/linux/net/socket.c linux/net/socket.c
--- v2.0.35/linux/net/socket.c	Mon Jul 13 13:46:43 1998
+++ linux/net/socket.c	Sun Nov 15 10:33:23 1998
@@ -61,6 +61,7 @@
 #include <linux/stat.h>
 #include <linux/socket.h>
 #include <linux/fcntl.h>
+#include <linux/file.h>
 #include <linux/net.h>
 #include <linux/interrupt.h>
 #include <linux/netdevice.h>
@@ -88,6 +89,7 @@
 		      int size);
 
 static void sock_close(struct inode *inode, struct file *file);
+static int sock_no_open(struct inode *inode, struct file *file);
 static int sock_select(struct inode *inode, struct file *file, int which, select_table *seltable);
 static int sock_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg);
@@ -107,7 +109,7 @@
 	sock_select,
 	sock_ioctl,
 	NULL,			/* mmap */
-	NULL,			/* no special open code... */
+	sock_no_open,		/* special open code... */
 	sock_close,
 	NULL,			/* no fsync */
 	sock_fasync
@@ -216,22 +218,38 @@
  *	Go from a file number to its socket slot.
  */
 
-extern __inline struct socket *sockfd_lookup(int fd, struct file **pfile)
+extern __inline struct socket *sockfd_lookup(int fd, int *err)
 {
 	struct file *file;
 	struct inode *inode;
+	struct socket *sock;
 
-	if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd])) 
+	if (!(file = fget(fd)))
+	{
+		*err = -EBADF;
 		return NULL;
+	}
 
 	inode = file->f_inode;
-	if (!inode || !inode->i_sock)
-		return NULL;
 
-	if (pfile) 
-		*pfile = file;
+	if (!inode || !inode->i_sock || !(sock = socki_lookup(inode)))
+	{
+		*err = -ENOTSOCK;
+		fput(file, inode);
+		return NULL;
+	}
+	
+	if (sock->file != file) {
+		printk(KERN_ERR "socki_lookup: socket file changed!\n");
+		sock->file = file;
+	}
+	
+	return sock;
+}
 
-	return socki_lookup(inode);
+extern __inline__ void sockfd_put(struct socket *sock)
+{
+	fput(sock->file, sock->file->f_inode);
 }
 
 /*
@@ -264,6 +282,7 @@
 	sock->wait = &inode->i_wait;
 	sock->inode = inode;		/* "backlink": we could use pointer arithmetic instead */
 	sock->fasync_list = NULL;
+	sock->file = NULL;
 	sockets_in_use++;
 	return sock;
 }
@@ -279,6 +298,17 @@
 	sock_wake_async(peer, 1);
 }
 
+/*
+ *	In theory you can't get an open on this inode, but /proc provides
+ *	a back door. Remember to keep it shut otherwise you'll let the
+ *	creepy crawlies in.
+ */
+  
+static int sock_no_open(struct inode *inode, struct file *file)
+{
+	return -ENXIO;
+}
+
 void sock_release(struct socket *sock)
 {
 	int oldstate;
@@ -613,9 +643,10 @@
 
 	if ((fd1 = sys_socket(family, type, protocol)) < 0) 
 		return(fd1);
-	sock1 = sockfd_lookup(fd1, NULL);
+	sock1 = sockfd_lookup(fd1, &er);
 	if (!sock1->ops->socketpair) 
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
 		return(-EINVAL);
 	}
@@ -626,14 +657,17 @@
 
 	if ((fd2 = sys_socket(family, type, protocol)) < 0) 
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
 		return(-EINVAL);
 	}
 
-	sock2 = sockfd_lookup(fd2, NULL);
+	sock2 = sockfd_lookup(fd2, &er);
 	if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) 
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
+		sockfd_put(sock2);
 		sys_close(fd2);
 		return(i);
 	}
@@ -646,13 +680,17 @@
 	er=verify_area(VERIFY_WRITE, usockvec, sizeof(usockvec));
 	if(er)
 	{
+		sockfd_put(sock1);
 		sys_close(fd1);
+		sockfd_put(sock2);
 		sys_close(fd2);
 	 	return er;
 	}
 	put_user(fd1, &usockvec[0]);
 	put_user(fd2, &usockvec[1]);
 
+	sockfd_put(sock1);
+	sockfd_put(sock2);
 	return(0);
 }
 
@@ -668,24 +706,20 @@
 asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen)
 {
 	struct socket *sock;
-	int i;
 	char address[MAX_SOCK_ADDR];
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
   
 	if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0)
-	  	return err;
+	  	goto out;
   
-	if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0) 
-	{
-		return(i);
-	}
-	return(0);
+	if ((err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) > 0) 
+		err = 0;
+out:		
+	sockfd_put(sock);
+	return err;
 }
 
 
@@ -700,20 +734,23 @@
 	struct socket *sock;
 	int err=-EOPNOTSUPP;
 	
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 
 	if (sock->state != SS_UNCONNECTED) 
-		return(-EINVAL);
-
+	{
+		err=-EINVAL;
+		goto out;
+	}
+	
 	if (sock->ops && sock->ops->listen)
 	{
 		err=sock->ops->listen(sock, backlog);
 		if(!err)
 			sock->flags |= SO_ACCEPTCON;
 	}
+out:
+	sockfd_put(sock);
 	return(err);
 }
 
@@ -728,28 +765,23 @@
 
 asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
 {
-	struct file *file;
 	struct socket *sock, *newsock;
 	int i;
 	char address[MAX_SOCK_ADDR];
 	int len;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-  	if (!(sock = sockfd_lookup(fd, &file))) 
-		return(-ENOTSOCK);
-	if (sock->state != SS_UNCONNECTED) 
-	{
-		return(-EINVAL);
-	}
-	if (!(sock->flags & SO_ACCEPTCON)) 
+  	if (!(sock = sockfd_lookup(fd, &i))) 
+		return i;
+	if (sock->state != SS_UNCONNECTED || (!(sock->flags & SO_ACCEPTCON)))
 	{
+		sockfd_put(sock);
 		return(-EINVAL);
 	}
 
 	if (!(newsock = sock_alloc())) 
 	{
 		printk(KERN_WARNING "accept: no more sockets\n");
+		sockfd_put(sock);
 		return(-ENOSR);	/* Was: EAGAIN, but we are out of system
 				   resources! */
 	}
@@ -758,19 +790,22 @@
 	if ((i = sock->ops->dup(newsock, sock)) < 0) 
 	{
 		sock_release(newsock);
+		sockfd_put(sock);
 		return(i);
 	}
 
-	i = newsock->ops->accept(sock, newsock, file->f_flags);
+	i = newsock->ops->accept(sock, newsock, sock->file->f_flags);
 	if ( i < 0) 
 	{
 		sock_release(newsock);
+		sockfd_put(sock);
 		return(i);
 	}
 
 	if ((fd = get_fd(SOCK_INODE(newsock))) < 0) 
 	{
 		sock_release(newsock);
+		sockfd_put(sock);
 		return(-EINVAL);
 	}
 	newsock->file=current->files->fd[fd];
@@ -780,6 +815,7 @@
 		newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
 		move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
 	}
+	sockfd_put(sock);
 	return(fd);
 }
 
@@ -792,18 +828,14 @@
 asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
 {
 	struct socket *sock;
-	struct file *file;
-	int i;
 	char address[MAX_SOCK_ADDR];
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || (file=current->files->fd[fd]) == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, &file)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return(err);
 
 	if((err=move_addr_to_kernel(uservaddr,addrlen,address))<0)
-	  	return err;
+	  	goto out;
   
 	switch(sock->state) 
 	{
@@ -814,7 +846,8 @@
 			/* Socket is already connected */
 			if(sock->type == SOCK_DGRAM) /* Hack for now - move this all into the protocol */
 				break;
-			return -EISCONN;
+			err = -EISCONN;
+			goto out;
 		case SS_CONNECTING:
 			/* Not yet connected... we will check this. */
 		
@@ -825,14 +858,15 @@
 			 */
 			break;
 		default:
-			return(-EINVAL);
-	}
-	i = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, file->f_flags);
-	if (i < 0) 
-	{
-		return(i);
+			err = -EINVAL;
+			goto out;
 	}
-	return(0);
+	err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, sock->file->f_flags);
+	if (err > 0) 
+		err = 0;
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -847,17 +881,17 @@
 	int len;
 	int err;
 	
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 0);
 	if(err)
-		return err;
-	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0)
-	  	return err;
-	return 0;
+		goto out;
+	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))>0)
+		err = 0;
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -872,17 +906,17 @@
 	int len;
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 1);
 	if(err)
-	  	return err;
-	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0)
-	  	return err;
-	return 0;
+	  	goto out;
+	if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))>0)
+		err = 0;
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -893,29 +927,28 @@
 asmlinkage int sys_send(int fd, void * buff, int len, unsigned flags)
 {
 	struct socket *sock;
-	struct file *file;
 	int err;
 	struct msghdr msg;
 	struct iovec iov;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
-
 	if(len<0)
 		return -EINVAL;
 	err=verify_area(VERIFY_READ, buff, len);
 	if(err)
 		return err;
 		
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
+		
 	iov.iov_base=buff;
 	iov.iov_len=len;
 	msg.msg_name=NULL;
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_control=NULL;
-	return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags));
+	err=sock->ops->sendmsg(sock, &msg, len, (sock->file->f_flags & O_NONBLOCK), flags);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -928,22 +961,18 @@
 	   struct sockaddr *addr, int addr_len)
 {
 	struct socket *sock;
-	struct file *file;
 	char address[MAX_SOCK_ADDR];
 	int err;
 	struct msghdr msg;
 	struct iovec iov;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-
 	if(len<0)
 		return -EINVAL;
 	err=verify_area(VERIFY_READ,buff,len);
 	if(err)
 	  	return err;
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	iov.iov_base=buff;
 	iov.iov_len=len;
@@ -955,13 +984,17 @@
 	if (addr && addr_len) {
 		err=move_addr_to_kernel(addr,addr_len,address);
 		if (err < 0)
+		{
+			sockfd_put(sock);
 			return err;
+		}
 		msg.msg_name=address;
 		msg.msg_namelen=addr_len;
 	}
 	  	
-	return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK),
-		flags));
+	err=sock->ops->sendmsg(sock, &msg, len, (sock->file->f_flags & O_NONBLOCK), flags);
+	sockfd_put(sock);
+	return err;
 }
 
 
@@ -974,15 +1007,8 @@
 	struct iovec iov;
 	struct msghdr msg;
 	struct socket *sock;
-	struct file *file;
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
-		
 	if(size<0)
 		return -EINVAL;
 	if(size==0)
@@ -990,6 +1016,8 @@
 	err=verify_area(VERIFY_WRITE, ubuf, size);
 	if(err)
 		return err;
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 		
 	msg.msg_name=NULL;
 	msg.msg_iov=&iov;
@@ -998,7 +1026,9 @@
 	iov.iov_base=ubuf;
 	iov.iov_len=size;
 
-	return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen));
+	err=sock->ops->recvmsg(sock, &msg, size,(sock->file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -1011,24 +1041,20 @@
 	     struct sockaddr *addr, int *addr_len)
 {
 	struct socket *sock;
-	struct file *file;
 	struct iovec iov;
 	struct msghdr msg;
 	char address[MAX_SOCK_ADDR];
 	int err;
 	int alen;
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-	  	return(-ENOTSOCK);
 	if(size<0)
 		return -EINVAL;
 	if(size==0)
 		return 0;
-
 	err=verify_area(VERIFY_WRITE,ubuf,size);
 	if(err)
 	  	return err;
+	if (!(sock = sockfd_lookup(fd, &err))) 
+	  	return err;
   
   	msg.msg_control=NULL;
   	msg.msg_iovlen=1;
@@ -1037,9 +1063,11 @@
   	iov.iov_base=ubuf;
   	msg.msg_name=address;
   	msg.msg_namelen=MAX_SOCK_ADDR;
-	size=sock->ops->recvmsg(sock, &msg, size, (file->f_flags & O_NONBLOCK),
+	size=sock->ops->recvmsg(sock, &msg, size, (sock->file->f_flags & O_NONBLOCK),
 		     flags, &alen);
 
+	sockfd_put(sock);
+
 	if(size<0)
 	 	return size;
 	if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)
@@ -1056,14 +1084,14 @@
 asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen)
 {
 	struct socket *sock;
-	struct file *file;
+	int err;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 
-	return(sock->ops->setsockopt(sock, level, optname, optval, optlen));
+	err=sock->ops->setsockopt(sock, level, optname, optval, optlen);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -1074,16 +1102,19 @@
 asmlinkage int sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen)
 {
 	struct socket *sock;
-	struct file *file;
+	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-	    
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+		
 	if (!sock->ops->getsockopt) 
+	{
+		sockfd_put(sock);
 		return(0);
-	return(sock->ops->getsockopt(sock, level, optname, optval, optlen));
+	}
+	err=sock->ops->getsockopt(sock, level, optname, optval, optlen);
+	sockfd_put(sock);
+	return err;
 }
 
 
@@ -1094,14 +1125,13 @@
 asmlinkage int sys_shutdown(int fd, int how)
 {
 	struct socket *sock;
-	struct file *file;
-
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	int err;
 
-	return(sock->ops->shutdown(sock, how));
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
+	err=sock->ops->shutdown(sock, how);
+	sockfd_put(sock);
+	return err;
 }
 
 /*
@@ -1111,39 +1141,43 @@
 asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned int flags)
 {
 	struct socket *sock;
-	struct file *file;
 	char address[MAX_SOCK_ADDR];
 	struct iovec iov[UIO_MAXIOV];
 	struct msghdr msg_sys;
 	int err;
 	int total_len;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-	
-	if(sock->ops->sendmsg==NULL)
-		return -EOPNOTSUPP;
-
-
 	err=verify_area(VERIFY_READ, msg,sizeof(struct msghdr));
 	if(err)
 		return err;
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+	if(sock->ops->sendmsg==NULL)
+	{
+		err = -EOPNOTSUPP;
+		goto out;
+	}
 
 	memcpy_fromfs(&msg_sys,msg,sizeof(struct msghdr));
 
 	/* do not move before msg_sys is valid */
 	if(msg_sys.msg_iovlen>UIO_MAXIOV)
-		return -EINVAL;
+	{
+		err = -EINVAL;
+		goto out;
+	}
 
 	/* This will also move the address data into kernel space */
 	err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
 	if (err < 0)
-		return err;
+		goto out;
+
 	total_len=err;
 
-	return sock->ops->sendmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags);
+	err=sock->ops->sendmsg(sock, &msg_sys, total_len, (sock->file->f_flags&O_NONBLOCK), flags);
+out:
+	sockfd_put(sock);
+	return err;	
 }
 
 /*
@@ -1153,7 +1187,6 @@
 asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags)
 {
 	struct socket *sock;
-	struct file *file;
 	struct iovec iov[UIO_MAXIOV];
 	struct msghdr msg_sys;
 	int err;
@@ -1168,11 +1201,6 @@
 	struct sockaddr *uaddr;
 	int *uaddr_len;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-	
 	err=verify_area(VERIFY_READ, msg,sizeof(struct msghdr));
 	if(err)
 		return err;
@@ -1180,6 +1208,10 @@
 	if(msg_sys.msg_iovlen>UIO_MAXIOV)
 		return -EINVAL;
 
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+	
+
 	/*
 	 * save the user-mode address (verify_iovec will change the
 	 * kernel msghdr to use the kernel address space)
@@ -1188,22 +1220,31 @@
 	uaddr_len = &msg->msg_namelen;
 	err=verify_iovec(&msg_sys,iov,addr, VERIFY_WRITE);
 	if(err<0)
-		return err;
+		goto out;
 
 	total_len=err;
 	
 	if(sock->ops->recvmsg==NULL)
-		return -EOPNOTSUPP;
-	len=sock->ops->recvmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags, &addr_len);
-	if(len<0)
-		return len;
+	{
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+	err=sock->ops->recvmsg(sock, &msg_sys, total_len, (sock->file->f_flags&O_NONBLOCK), flags, &addr_len);
+	if(err<0)
+		goto out;
+	len=err;
 
 	if (uaddr != NULL) {
 		err = move_addr_to_user(addr, addr_len, uaddr, uaddr_len);
 		if (err)
-			return err;
+			goto out;
 	}
+	sockfd_put(sock);
 	return len;
+	
+out:
+	sockfd_put(sock);
+	return err;
 }
 
 
diff -u --recursive --new-file v2.0.35/linux/net/unix/garbage.c linux/net/unix/garbage.c
--- v2.0.35/linux/net/unix/garbage.c	Tue Mar 10 13:19:10 1998
+++ linux/net/unix/garbage.c	Sun Nov 15 10:33:23 1998
@@ -39,6 +39,25 @@
  *	2 of the License, or (at your option) any later version.
  *
  *  Fixes:
+ *     Al Viro         11 Oct 1998
+ *             Graph may have cycles. That is, we can send the descriptor
+ *             of foo to bar and vice versa. Current code chokes on that.
+ *             Fix: move SCM_RIGHTS ones into the separate list and then
+ *             kfree_skb() them all instead of doing explicit fput's.
+ *             Another problem: since fput() may block somebody may
+ *             create a new unix_socket when we are in the middle of sweep
+ *             phase. Fix: revert the logic wrt MARKED. Mark everything
+ *             upon the beginning and unmark non-junk ones.
+ *
+ *             [12 Oct 1998] AAARGH! New code purges all SCM_RIGHTS
+ *             sent to connect()'ed but still not accept()'ed sockets.
+ *             Fixed. Old code had slightly different problem here:
+ *             extra fput() in situation when we passed the descriptor via
+ *             such socket and closed it (descriptor). That would happen on
+ *             each unix_gc() until the accept(). Since the struct file in
+ *             question would go to the free list and might be reused...
+ *             That might be the reason of random oopses on close_fp() in
+ *             unrelated processes.
  *
  */
  
@@ -136,11 +155,11 @@
 	return in_stack == 0;
 }
 
-extern inline void maybe_mark_and_push(unix_socket *x)
+extern inline void maybe_unmark_and_push(unix_socket *x)
 {
-	if (x->protinfo.af_unix.marksweep&MARKED)
+	if (!(x->protinfo.af_unix.marksweep&MARKED))
 		return;
-	x->protinfo.af_unix.marksweep|=MARKED;
+	x->protinfo.af_unix.marksweep&=~MARKED;
 	push_stack(x);
 }
 
@@ -151,8 +170,9 @@
 {
 	static int in_unix_gc=0;
 	unix_socket *s;
-	unix_socket *next;
-	
+	struct sk_buff *skb;
+	struct sk_buff_head hitlist;
+		
 	/*
 	 *	Avoid a recursive GC.
 	 */
@@ -170,16 +190,21 @@
 	}
 	
 	/*
-	 *	Assume everything is now unmarked 
+	 *	Everything is now marked
 	 */
 
+	for(s=unix_socket_list;s!=NULL;s=s->next)
+	{
+		s->protinfo.af_unix.marksweep|=MARKED;
+	}
+	
 	/* Invariant to be maintained:
-		- everything marked is either:
+		- everything unmarked is either:
 		-- (a) on the stack, or
-		-- (b) has all of its children marked
-		- everything on the stack is always marked
+		-- (b) has all of its children unmarked
+		- everything on the stack is always unmarked
 		- nothing is ever pushed onto the stack twice, because:
-		-- nothing previously marked is ever pushed on the stack
+		-- nothing previously unmarked is ever pushed on the stack
 	 */
 
 	/*
@@ -192,19 +217,19 @@
 		 *	If all instances of the descriptor are not
 		 *	in flight we are in use.
 		 */
-		if(s->socket && s->socket->file && s->socket->file->f_count > s->protinfo.af_unix.inflight)
-			maybe_mark_and_push(s);
+		if(s->socket && s->socket->file && 
+			s->socket->file->f_count > s->protinfo.af_unix.inflight)
+			maybe_unmark_and_push(s);
 	}
 
 	/*
-	 *	Mark phase 
+	 *	Mark phase
 	 */
 
 	while (!empty_stack())
 	{
 		unix_socket *x = pop_stack();
 		unix_socket *f=NULL,*sk;
-		struct sk_buff *skb;
 tail:		
 		skb=skb_peek(&x->receive_queue);
 		
@@ -233,27 +258,38 @@
 					if((sk=unix_get_socket(*fp++))!=NULL)
 					{
 						/*
-						 *	Remember the first, mark the
+						 *	Remember the first, unmark the
 						 *	rest.
 						 */
 						if(f==NULL)
 							f=sk;
 						else
-							maybe_mark_and_push(sk);
+							maybe_unmark_and_push(sk);
 					}
 				}
 			}
+			/* 
+			 *	If we are connecting we need to handle this too
+			 */
+			if(x->state == TCP_LISTEN)
+			{
+				if(f==NULL)
+					f=skb->sk;
+				else
+					maybe_unmark_and_push(skb->sk);
+			}			 
 			skb=skb->next;
 		}
+
 		/*
 		 *	Handle first born specially 
 		 */
 
 		if (f) 
 		{
-			if (!(f->protinfo.af_unix.marksweep&MARKED))
+			if (f->protinfo.af_unix.marksweep&MARKED)
 			{
-				f->protinfo.af_unix.marksweep|=MARKED;
+				f->protinfo.af_unix.marksweep&=~MARKED;
 				x=f;
 				f=NULL;
 				goto tail;
@@ -261,35 +297,44 @@
 		}
 	}
 
-	/*
-	 *	Sweep phase.  NOTE: this part dominates the time complexity 
-	 */
+	skb_queue_head_init(&hitlist);
 
-	for(s=unix_socket_list;s!=NULL;s=next)
+	for(s=unix_socket_list;s!=NULL;s=s->next)
 	{
-		next=s->next;
-		if (!(s->protinfo.af_unix.marksweep&MARKED))
+		if (s->protinfo.af_unix.marksweep&MARKED)
 		{
-			/*
-			 *	We exist only in the passing tree of sockets
-			 *	that is no longer connected to active descriptors
-			 *	Time to die..
-			 *
-			 *	Subtle item: We will correctly sweep out the
-			 *	socket that has just been closed by the user.
-			 *	We must not close this as we are in the middle
-			 *	of its close at this moment. Skip that file
-			 *	using f_count==0 to spot it.
-			 */
-			 
-			if(s->socket && s->socket->file && s->socket->file->f_count)
-				close_fp(s->socket->file);
+			struct sk_buff *nextsk;
+			skb=skb_peek(&s->receive_queue);
+			while(skb && skb != (struct sk_buff *)&s->receive_queue)
+			{
+				nextsk=skb->next;
+				/*
+				 *      Do we have file descriptors ?
+ 				 */
+				if(*(int *)(skb->h.filp))
+				{
+					/* 
+					 *	Pull these buffers out of line
+					 *	so they will each be freed once
+					 *	at the end.
+					 */       	
+					skb_unlink(skb);
+					skb_queue_tail(&hitlist,skb);
+				}
+				skb=nextsk;
+			}
 		}
-		else
-			s->protinfo.af_unix.marksweep&=~MARKED;	/* unmark everything for next collection */
 	}
-	
+
+	/*
+	 *      Here we are. Hitlist is filled. Die.
+	 */
+
+	while ((skb=skb_dequeue(&hitlist))!=NULL) 
+	{
+		kfree_skb(skb, FREE_READ);
+	}
+
 	in_unix_gc=0;
-	
 	vfree(stack);
 }
diff -u --recursive --new-file v2.0.35/linux/scripts/checkconfig.pl linux/scripts/checkconfig.pl
--- v2.0.35/linux/scripts/checkconfig.pl	Wed Dec 31 16:00:00 1969
+++ linux/scripts/checkconfig.pl	Sun Nov 15 10:33:23 1998
@@ -0,0 +1,53 @@
+#! /usr/bin/perl
+#
+# checkconfig: find uses of CONFIG_* names without matching definitions.
+# Copyright abandoned, 1998, Michael Elizabeth Chastain <mailto:mec@shout.net>.
+
+use integer;
+
+$| = 1;
+
+foreach $file (@ARGV)
+{
+    # Open this file.
+    open(FILE, $file) || die "Can't open $file: $!\n";
+
+    # Initialize variables.
+    my $fInComment   = 0;
+    my $fUseConfig   = 0;
+    my $iLinuxConfig = 0;
+    my %configList   = ();
+
+    LINE: while ( <FILE> )
+    {
+	# Strip comments.
+	$fInComment && (s+^.*?\*/+ +o ? ($fInComment = 0) : next);
+	m+/\*+o && (s+/\*.*?\*/+ +go, (s+/\*.*$+ +o && ($fInComment = 1)));
+
+	# Pick up definitions.
+	if ( m/^#/o )
+	{
+	    $iLinuxConfig      = $. if m/^#\s*include\s*<linux\/config\.h>/o;
+	    $configList{uc $1} = 1  if m/^#\s*include\s*<config\/(\S*)\.h>/o;
+	    $configList{$1}    = 1  if m/^#\s*define\s+CONFIG_(\w*)/o;
+	    $configList{$1}    = 1  if m/^#\s*undef\s+CONFIG_(\w*)/o;
+	}
+
+	# Look for usages.
+	next unless m/CONFIG_/o;
+	WORD: while ( m/\bCONFIG_(\w+)/og )
+	{
+	    $fUseConfig = 1;
+	    last LINE if $iLinuxConfig;
+	    next WORD if exists $configList{$1};
+	    print "$file: $.: need CONFIG_$1.\n";
+	    $configList{$1} = 0;
+	}
+    }
+
+    # Report superfluous includes.
+    if ( $iLinuxConfig && ! $fUseConfig )
+	{ print "$file: $iLinuxConfig: <linux/config.h> not needed.\n"; }
+
+    close(FILE);
+}
diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/dialog.h linux/scripts/lxdialog/dialog.h
--- v2.0.35/linux/scripts/lxdialog/dialog.h	Thu Apr 18 04:38:54 1996
+++ linux/scripts/lxdialog/dialog.h	Sun Nov 15 10:33:24 1998
@@ -28,6 +28,24 @@
 
 #include CURSES_LOC
 
+/*
+ * Colors in ncurses 1.9.9e do not work properly since foreground and
+ * background colors are OR'd rather than separately masked.  This version
+ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+ * with standard curses.  The simplest fix (to make this work with standard
+ * curses) uses the wbkgdset() function, not used in the original hack.
+ * Turn it off if we're building with 1.9.9e, since it just confuses things.
+ */
+#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+#define OLD_NCURSES 1
+#undef  wbkgdset
+#define wbkgdset(w,p) /*nothing*/
+#else
+#define OLD_NCURSES 0
+#endif
+
+#define TR(params) _tracef params
+
 #define ESC 27
 #define TAB 9
 #define MAX_LEN 2048
diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c
--- v2.0.35/linux/scripts/lxdialog/lxdialog.c	Sun Aug  3 15:38:39 1997
+++ linux/scripts/lxdialog/lxdialog.c	Sun Nov 15 10:33:24 1998
@@ -63,6 +63,9 @@
     (void) setlocale (LC_ALL, "");
 #endif
 
+#ifdef TRACE
+    trace(TRACE_CALLS|TRACE_UPDATE);
+#endif
     if (argc < 2) {
 	Usage (argv[0]);
 	exit (-1);
diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/menubox.c linux/scripts/lxdialog/menubox.c
--- v2.0.35/linux/scripts/lxdialog/menubox.c	Sat Apr 27 03:18:18 1996
+++ linux/scripts/lxdialog/menubox.c	Sun Nov 15 10:33:24 1998
@@ -39,8 +39,12 @@
     /* Clear 'residue' of last item */
     wattrset (win, menubox_attr);
     wmove (win, choice, 0);
+#if OLD_NCURSES
     for (i = 0; i < menu_width; i++)
 	waddch (win, ' ');
+#else
+    wclrtoeol(win);
+#endif
     wattrset (win, selected ? item_selected_attr : item_attr);
     mvwaddstr (win, choice, item_x, menu_item);
     if (hotkey) {
@@ -141,6 +145,7 @@
     for (i = 0; i < width - 2; i++)
 	waddch (dialog, ACS_HLINE);
     wattrset (dialog, dialog_attr);
+    wbkgdset (dialog, dialog_attr & A_COLOR);
     waddch (dialog, ACS_RTEE);
 
     if (title != NULL) {
diff -u --recursive --new-file v2.0.35/linux/scripts/lxdialog/textbox.c linux/scripts/lxdialog/textbox.c
--- v2.0.35/linux/scripts/lxdialog/textbox.c	Tue Feb  6 23:47:28 1996
+++ linux/scripts/lxdialog/textbox.c	Sun Nov 15 10:33:24 1998
@@ -90,6 +90,8 @@
 
     /* Create window for text region, used for scrolling text */
     text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
+    wattrset (text, dialog_attr);
+    wbkgdset (text, dialog_attr & A_COLOR);
 
     keypad (text, TRUE);
 
@@ -101,6 +103,7 @@
     for (i = 0; i < width - 2; i++)
 	waddch (dialog, ACS_HLINE);
     wattrset (dialog, dialog_attr);
+    wbkgdset (dialog, dialog_attr & A_COLOR);
     waddch (dialog, ACS_RTEE);
 
     if (title != NULL) {
@@ -459,8 +462,12 @@
 
     getyx (win, y, x);
     /* Clear 'residue' of previous line */
+#if OLD_NCURSES
     for (i = 0; i < width - x; i++)
 	waddch (win, ' ');
+#else
+    wclrtoeol(win);
+#endif
 }
 
 /*
@@ -530,6 +537,7 @@
 	exit (-1);
     }
     wattrset (win, position_indicator_attr);
+    wbkgdset (win, position_indicator_attr & A_COLOR);
     percent = !file_size ?
 	100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
     wmove (win, height - 3, width - 9);
