diff -u --recursive --new-file v2.1.26/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.26/linux/Documentation/Changes	Mon Feb  3 03:06:10 1997
+++ linux/Documentation/Changes	Fri Feb 21 14:58:32 1997
@@ -26,7 +26,7 @@
 http://www.datanet.hu/generations/linux/Changes2.html is an
 English-language HTML version.
 
-Last updated: January 29, 1997.
+Last updated: February 11, 1997.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Minimal Requirements
@@ -47,6 +47,7 @@
 - Net-tools              1.32-alpha
 - Kbd                    0.91
 - Loadlin                1.6a
+- Sh-utils               1.16
 
 Upgrade notes
 *************
@@ -136,6 +137,13 @@
    Linux 2.1.22 and later releases use a new method of memory size
 detection, requiring loadlin users to upgrade to loadlin-1.6a.
 
+Sh-utils
+========
+
+   As of Linux-2.1.26, the Configure script ("make config") has been
+updated to be POSIX-compliant.  As a result, your expr needs to be
+updated.  Use sh-utils 1.16 or later.
+
 How to know the version of the installed programs
 *************************************************
 
@@ -155,6 +163,7 @@
 SysVinit: strings `egrep -li INIT_VERSION=sysvinit- /proc/*/environ | head
 -1` | egrep -i INIT_VERSION=sysvinit-
 RPM:  rpm --version
+Sh-utils:  expr --v
 
 Where to get the files
 **********************
@@ -253,6 +262,13 @@
 The 1.6a release:
 ftp://ftp.suse.com/pub/loadlin/update-1.6a/loadlin.exe.gz
 ftp://elserv.ffm.fgan.de/pub/linux/loadlin-1.6/update-1.6a/loadlin.exe.gz
+
+Sh-utils
+========
+
+The 1.16 release
+ftp://sunsite.unc.edu/pub/gnu/sh-utils-1.16.tar.gz
+ftp://prep.ai.mit.edu/pub/gnu/sh-utils-1.16.tar.gz
 
 Other Info
 ==========
diff -u --recursive --new-file v2.1.26/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.26/linux/Documentation/Configure.help	Tue Feb  4 06:44:24 1997
+++ linux/Documentation/Configure.help	Tue Feb 25 17:12:49 1997
@@ -1217,8 +1217,8 @@
 IP-over-DDP support
 CONFIG_IPDDP
   This allows IP networking for users who only have Appletalk
-  networking available.  It doesn't work yet in 2.1.xx, so you
-  should say N.
+  networking available.  This feature is experimental. Please see
+  http://www.maths.unm.edu/~bradford/ltpc.html
 
 LocalTalk PC card support
 CONFIG_LTPC
@@ -1229,7 +1229,8 @@
   This driver is experimental, which means that it may not work.
   In particular the module support is not yet working for the 2.1.xx
   kernels, so choose Y or N, but not M for now.
-  See README.ltpc in the drivers/net directory.
+  See README.ltpc in the drivers/net directory, and the web site
+  http://www.math.unm.edu/~bradford/ltpc.html
 
 Amateur Radio AX.25 Level 2
 CONFIG_AX25
@@ -2214,26 +2215,12 @@
   compatible to popular modems using TCM3105 or AM7911. The demodulator
   requires about 12% of the CPU power of a Pentium 75 CPU per channel.
 
-Soundmodem 1200 baud AFSK using floating point
-CONFIG_SOUNDMODEM_AFSK1200_FP
-  This option enables floating point calculations to be used for the
-  AFSK1200 baud modem. The Intel Pentium is a perverted chip because
-  integer multiplications are, although easier to implement in silicon,
-  an order of magnitude slower than floating point calculations.
-  Enabling this option uses a highly optimized assembler routine for
-  correlations, modeled after the one published by Phil Karn, KA9Q.
-  This reduces the computing power needed on Intel Pentium chips to
-  about 50%. On the other hand, Pentium clones with faster integer
-  multiply and slower floating point multiply will probably take
-  longer with this option turned on. As a rule of thumb, enable it for
-  Intel Pentium and Pentium Pro processors, and disable it for
-  anything else.
-  I (sailer@ife.ee.ethz.ch) am very interested in figures. If you are
-  willing to give me a feedback, please compile the driver once with
-  this option enabled and once with it disabled, and send me the cycle
-  counter numbers obtained with both compilations, and your exact
-  chip. The cycle counter numbers can be obtained with a recent
-  sethdlc utility.
+Soundcard modem support for 4800 baud HAPN-1 modulation
+CONFIG_SOUNDMODEM_HAPN4800
+  This option enables the soundmodem driver 4800 baud HAPN-1 compatible
+  modem. This modulation seems to be widely used 'down under' and in
+  the netherlands. Here, nobody uses it, so I could not test if it works.
+  It is compatible to itself, however :-)
 
 Soundcard modem support for 9600 baud FSK G3RUH modulation
 CONFIG_SOUNDMODEM_FSK9600
@@ -2244,6 +2231,25 @@
   can only use one protocol at a time, depending on what the other end
   can understand).
 
+Soundcard modem support using floating point arithmetic
+CONFIG_SOUNDMODEM_FLOAT
+  This option enables floating point calculations to be used for the
+  AFSK1200 baud modem. The Intel Pentium is a perverted chip because
+  integer multiplications are, altough easier to implement in silicon,
+  an order of a magnitude slower than floating point calculations.
+  Enabling this option uses a highly optimized assembler routine for
+  correlations, modelled after the one published by Phil Karn, KA9Q.
+  This reduces the computing power needed on Intel Pentium chips to about
+  50%. On the other hand, Pentium clones with faster integer multiply and
+  slower floating point multiply will probably take longer with this
+  option turned on. As a rule of thumb, enable it for Intel Pentium and
+  Pentium Pro processors, and disable it for anything else.
+  I (sailer@ife.ee.ethz.ch) am very interested in figures. If you are
+  willing to give me a feedback, please compile the driver once with this
+  option enabled and once with it disabled, and send me the cycle counter
+  numbers obtained with both compilations, and your exact chip. The cycle
+  counter numbers can be optained by a recent sethdlc utility.
+
 Serial port KISS driver for AX.25
 CONFIG_MKISS
   KISS is the protocol used to send IP traffic over AX.25 radio
@@ -4473,8 +4479,8 @@
   (mgetty+sendfax by gert@greenie.muc.de with an extension, available
   with the ISDN utility package for example), you will be able to use
   your Linux box as an ISDN-answering machine. Of course, this must be
-  supported by the lowlevel driver also. Currently, the Teles driver
-  is the only voice-supporting one. See
+  supported by the lowlevel driver also. Currently, the Teles and HiSax
+  drivers are the only voice-supporting drivers. See
   Documentation/isdn/README.audio for more information.
 
 ICN 2B and 4B support
@@ -4493,14 +4499,77 @@
 Teles, NICCY1016PC, Creatix support
 CONFIG_ISDN_DRV_TELES
   This enables support for the Teles ISDN-cards S0-16.0, S0-16.3, S0-8
-  and many compatibles. By default, the driver is configured to
-  support a 16.0-type using EDSS1-protocol. See
-  Documentation/isdn/README on how to configure it using 16.3, a
-  different D-channel protocol, or non-standard irq/port/shmem
-  settings. If you want to compile this as a module ( = code which can
-  be inserted in and removed from the running kernel whenever you
-  want), say M here and read Documentation/modules.txt. The module
-  will be called teles.o.
+  and many compatibles.
+  There is a new, heavily improved driver called HiSax which can be
+  enabled in the next section. This driver will be removed soon. Please
+  use this driver only if you cannot get the HiSax driver working.
+  By default, the driver is configured to support a 16.0-type using
+  EDSS1-protocol. See Documentation/isdn/README on how to configure
+  it using 16.3, a different D-channel protocol, or non-standard
+  irq/port/shmem settings.
+
+HiSax SiemensChipSet driver support
+CONFIG_ISDN_DRV_HISAX
+  This is an alternative driver supporting the Siemens chipset 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.  So you either say M or Y here and N in the above Teles 
+  section.
+  See Documentation/isdn/README.HiSax for further informations on
+  using this driver.
+
+HiSax Support for Teles 16.0/8.0
+CONFIG_HISAX_16_0
+  This enables HiSax support for the Teles ISDN-cards S0-16.0,
+  S0-8 and many compatibles.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for Teles 16.3 or PNP or PCMCIA
+CONFIG_HISAX_16_3
+  This enables HiSax support for the Teles ISDN-cards S0-16.3
+  the Teles/Creatix PnP and the Teles PCMCIA.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for AVM A1 (Fritz)
+CONFIG_HISAX_AVM_A1
+  This enables HiSax support for the AVM A1 (aka "Fritz").
+  See Documentation/isdn/README.HiSax on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for Elsa cards
+CONFIG_HISAX_ELSA_PCC
+  This enables HiSax support for the Elsa Mircolink cards and
+  for the Elsa Quickstep series 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/shmem settings.
+
+HiSax Support for ITK ix1-micro Revision 2
+CONFIG_HISAX_IX1MICROR2
+  This enables HiSax support for the ITK ix1-micro Revision 2 card.
+  See Documentation/isdn/README.HiSax on how to configure it 
+  using the different cards, a different D-channel protocol, or
+  non-standard irq/port/shmem settings.
+
+HiSax Support for EURO/DSS1
+CONFIG_HISAX_EURO
+  You should choose your D-channel protocol your local
+  telephone service provider uses here by saying Y or N.
+  NOTE: This is mutually exclusive with HiSax Support for
+  german 1TR6 if you have only one ISDN card installed.
+
+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 if you have only one ISDN card installed.
 
 PCBIT-D support
 CONFIG_ISDN_DRV_PCBIT
@@ -4513,6 +4582,13 @@
   which can be inserted in and removed from the running kernel
   whenever you want), say M here and read
   Documentation/modules.txt. The module will be called pcbit.o.
+
+Spellcaster support (EXPERIMENTAL)
+CONFIG_ISDN_DRV_SC
+  This enables support for the Spellcaster BRI boards. This driver
+  currently builds in a modularized version only.
+  See Documentation/isdn/README.sc and http://www.spellcast.com
+  for more information.
 
 Support for AP1000 multicomputer
 CONFIG_AP1000
diff -u --recursive --new-file v2.1.26/linux/Documentation/isdn/CREDITS linux/Documentation/isdn/CREDITS
--- v2.1.26/linux/Documentation/isdn/CREDITS	Sun Apr 21 01:56:13 1996
+++ linux/Documentation/isdn/CREDITS	Tue Feb 25 17:12:49 1997
@@ -12,11 +12,18 @@
   For contribution of man-pages, the imontty-tool and a perfect
   maintaining of the mailing-list at hub-wue.
 
+Bernhard Hailer (Bernhard.Hailer@lrz.uni-muenchen.de)
+  For maintaining the FAQ.
+
+Michael 'Ghandi' Herold (michael@abadonna.franken.de)
+  For contribution of the vbox answering machine.
+
 Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
   For his Sync-PPP-code.
 
 Karsten Keil (isdn4@temic-ech.spacenet.de)
   For adding 1TR6-support to the Teles-driver.
+  For the HiSax-driver.
 
 Michael Knigge (knick@cove.han.de)
   For contributing the imon-tool
@@ -39,7 +46,7 @@
 Gerhard 'Fido' Schneider (fido@wuff.franken.de)
   For heavy-duty-beta-testing with his BBS ;)
 
-Thomas Uhl (uhl@hn-net.de)
+Thomas Uhl (uhl@think.de)
   For distributing the cards.
   For pushing me to work ;-)
 
diff -u --recursive --new-file v2.1.26/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE
--- v2.1.26/linux/Documentation/isdn/INTERFACE	Sat Jun 29 10:36:22 1996
+++ linux/Documentation/isdn/INTERFACE	Tue Feb 25 17:12:49 1997
@@ -1,4 +1,4 @@
-$Id: INTERFACE,v 1.3 1996/06/25 17:52:41 fritz Exp $
+$Id: INTERFACE,v 1.6 1997/02/10 22:40:57 fritz Exp $
 
 Description of the Interface between Linklevel and Hardwarelevel
   of isdn4linux:
@@ -140,7 +140,7 @@
 
   int (*writebuf)(int, int, u_char*, int, int);
 
-    ***CHANGEc1.14: Declared obsolete. Do NOT use this field/function
+    ***CHANGED1.14: Declared obsolete. Do NOT use this field/function
                     anymore, since it will be removed when all current
                     LL drivers have been changed accordingly. Set this
                     field to NULL and use writebuf_skb instead.
@@ -199,7 +199,7 @@
       int     driver-Id.
       int     channel-number locally to the HL-driver. (starts with 0)
 
-***CHANGEc1.14: The driver-Id and channel-number are new since this revision.
+***CHANGED1.14: The driver-Id and channel-number are new since this revision.
 
     Returnvalue:
       Length of data accepted on success, else error-code (-EINVAL etc.)
@@ -223,7 +223,7 @@
       int     driver-Id.
       int     channel-number locally to the HL-driver. (starts with 0)
 
-***CHANGEc1.14: The driver-Id and channel-number are new since this revision.
+***CHANGED1.14: The driver-Id and channel-number are new since this revision.
 
     Returnvalue:
       Length of data on success, else error-code (-EINVAL etc.)
@@ -249,6 +249,9 @@
 
    Until now, the following commands are defined:
 
+***CHANGED1.34: The parameter "num" has been replaced by a union "para" containing
+                the old "num" and a new setup_type struct used for ISDN_CMD_DIAL
+                and ISDN_STAT_ICALL callback.
 
    ISDN_CMD_IOCTL:
 
@@ -262,10 +265,10 @@
        called with the field command set to 1.
 
      Parameter:
-       driver  = driver-Id.
-       command = ISDN_CMD_IOCTL
-       arg     = Original ioctl-cmd - IIOCDRVCTL
-       num     = first bytes filled with (unsigned long)arg
+       driver   = driver-Id.
+       command  = ISDN_CMD_IOCTL
+       arg      = Original ioctl-cmd - IIOCDRVCTL
+       para.num = first bytes filled with (unsigned long)arg
    
      Returnvalue:
        Depending on driver.
@@ -280,12 +283,14 @@
       driver      = driver-Id.
       command     = ISDN_CMD_DIAL
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = An ASCII-String containing the number to dial, the own
-                    EAZ or MSN, the Service-Indicator and the Additional
-                    Info. Format:
-                    "%s,%s,%d,%d" RemotePhoneNumber,EazOrMsn,SI,AI
+      
+      para.setup.phone  = An ASCII-String containing the number to dial.
+      para.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN.
+      para.setup.si1    = The Service-Indicator.
+      para.setup.si2    = Additional Service-Indicator.
+
                     If the Line has been designed as SPV (a special german
-                    feature, meaning semi-leased-line) the number has to
+                    feature, meaning semi-leased-line) the phone has to
                     start with an "S".
       ***CHANGE0.6: In previous versions the EAZ has been given in the
                     highbyte of arg.
@@ -300,7 +305,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_ACCEPTD
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_ACCEPTB:
 
@@ -311,7 +316,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_ACCEPTB
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_HANGUP:
 
@@ -323,7 +328,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_HANGUP
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_CLREAZ:
 
@@ -334,7 +339,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_CLREAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_SETEAZ:
 
@@ -345,7 +350,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETEAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the desired EAZ's/MSN's
+      para.num    = ASCII-String, containing the desired EAZ's/MSN's
                     (comma-separated). If an empty String is given, the
                     HL-driver should respond to ALL incoming calls,
                     regardless of the destination-address.
@@ -360,7 +365,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETEAZ
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the current EAZ's/MSN's
+      para.num    = ASCII-String, containing the current EAZ's/MSN's
 
   ISDN_CMD_SETSIL: (currently unused)
 
@@ -371,7 +376,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETSIL
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the desired Service-Indicators.
+      para.num    = ASCII-String, containing the desired Service-Indicators.
 
   ISDN_CMD_GETSIL: (currently unused)
 
@@ -382,7 +387,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_SETSIL
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = ASCII-String, containing the current Service-Indicators.
+      para.num    = ASCII-String, containing the current Service-Indicators.
 
   ISDN_CMD_SETL2:
 
@@ -397,7 +402,7 @@
       arg         = channel-number locally to the driver. (starting with 0)
                     logical or'ed with (protocol-Id << 8)
                     protocol-Id is one of the constants ISDN_PROTO_L2...
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_GETL2: (currently unused)
 
@@ -408,7 +413,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETL2
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
     Returnvalue:
       current protocol-Id (one of the constants ISDN_L2_PROTO)
 
@@ -425,7 +430,7 @@
       arg         = channel-number locally to the driver. (starting with 0)
                     logical or'ed with (protocol-Id << 8)
                     protocol-Id is one of the constants ISDN_PROTO_L3...
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_GETL2: (currently unused)
 
@@ -436,7 +441,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_GETL3
       arg         = channel-number locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
     Returnvalue:
       current protocol-Id (one of the constants ISDN_L3_PROTO)
 
@@ -450,7 +455,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_LOCK
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_CMD_UNLOCK:
 
@@ -462,7 +467,7 @@
       driver      = driver-Id.
       command     = ISDN_CMD_UNLOCK
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
 3. Description of the events to be signaled by the HL-driver to th LL.
 
@@ -484,19 +489,29 @@
       driver      = driver-Id
       command     = ISDN_STAT_STAVAIL
       arg         = length of available data.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_ICALL:
 
     With this call, the HL-driver signals an incoming call to the LL.
 
     Parameter:
-      driver      = driver-Id
-      command     = ISDN_STAT_ICALL
-      arg         = channel-number, locally to the driver. (starting with 0)
-      num         = ASCII-String in the following format:
-                    "%s,%d,%d,%s",CallerNumber,ServiceIndicator,AddInfo,
-                    CalledNumber.
+      driver            = driver-Id
+      command           = ISDN_STAT_ICALL
+      arg               = channel-number, locally to the driver. (starting with 0)
+      para.setup.phone  = Callernumber.
+      para.setup.eazmsn = CalledNumber.
+      para.setup.si1    = Service Indicator.
+      para.setup.si2    = Additional Service Indicator.
+      para.setup.plan   = octet 3 from Calling party number Information Element.
+      para.setup.screen = octet 3a from Calling party number Information Element.
+
+    Return:
+      0           = No device matching this call.
+      1           = At least one device matching this call (RING on ttyI).
+                    HL-driver may send ALERTING on the D-channel in this case.
+      2           = Call will be rejected.
+      -1          = An error happened. (Invalid parameters for example.)
 
   ISDN_STAT_RUN:
 
@@ -507,7 +522,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_RUN
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_STOP:
 
@@ -518,7 +533,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_STOP
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_DCONN:
 
@@ -529,7 +544,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_DCONN
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_BCONN:
 
@@ -541,7 +556,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BCONN
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_DHUP:
 
@@ -554,7 +569,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_DHUP
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_BHUP:
 
@@ -566,7 +581,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BHUP
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_CINF:
 
@@ -577,7 +592,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_CINF
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = ASCII string containing charge-units (digits only).
+      para.num    = ASCII string containing charge-units (digits only).
 
   ISDN_STAT_LOAD: (currently unused)
 
@@ -590,7 +605,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_UNLOAD
       arg         = unused.
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_BSENT:
 
@@ -602,7 +617,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_BSENT
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_NODCH:
 
@@ -613,7 +628,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_NODCH
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = unused.
+      para        = unused.
 
   ISDN_STAT_ADDCH: (currently unused)
 
@@ -627,7 +642,7 @@
       driver      = driver-Id
       command     = ISDN_STAT_ADDCH
       arg         = to be defined.
-      num         = to be defined.
+      para        = to be defined.
 
   ISDN_STAT_CAUSE:
 
@@ -640,5 +655,5 @@
       driver      = driver-Id
       command     = ISDN_STAT_NODCH
       arg         = channel-number, locally to the driver. (starting with 0)
-      num         = ASCII string containing CAUSE-message.
+      para.num    = ASCII string containing CAUSE-message.
 
diff -u --recursive --new-file v2.1.26/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v2.1.26/linux/Documentation/isdn/README	Fri Jun  7 06:02:39 1996
+++ linux/Documentation/isdn/README	Tue Feb 25 17:12:49 1997
@@ -27,7 +27,14 @@
    subscribe isdn4linux <your_email_address>
 
    To write to the mailing-list, write to isdn4linux@hub-wue.franken.de
+
+   This mailinglist is bidirectionally gated to the newsgroup
+
+     de.alt.comm.isdn4linux
    
+  There is also a well maintained FAQ (both english and german) available
+  at ftp.franken.de in /pub/isdn4linux/FAQ/
+  This FAQ is also available at http://www.lrz-muenchen.de/~ui161ab/www/isdn/
 
   1.1 Technical details
 
@@ -89,6 +96,7 @@
 	       ATI      Return "ISDN for Linux...".
                ATI0        "
                ATI1        "
+			   ATI2 Report of last connection.
                ATO      On line (data mode).
                ATQ0     Enable result codes (default).
                ATQ1     Disable result codes (default).
@@ -173,6 +181,10 @@
                                       1 = T.70 protocol (Only for BTX!) on
                             Bit 2:    0 = Don't hangup on DTR low.
                                       1 = Hangup on DTR low.
+			    Bit 3:    0 = Standard response messages
+				      1 = Extended response messages
+                            Bit 4:    0 = CALLER NUMBER before every RING.
+                                      1 = CALLER NUMBER after first RING.
 	     14   0         Layer-2 protocol:
 				      0 = X75/LAPB with I-frames
 				      1 = X75/LAPB with UI-frames
@@ -201,6 +213,14 @@
              20   0         Bit coded register (readonly)
                             Service-Octet-1 of last call.
                             Bit mapping is the same like register 18
+             21   0         Bit coded register (readonly)
+                            Set on incoming call (during RING) to
+                            octet 3 of calling party number IE (Numbering plan)
+                            See section 4.5.10 of ITU Q.931
+             22   0         Bit coded register (readonly)
+                            Set on incoming call (during RING) to
+                            octet 3a of calling party number IE (Screening info)
+                            See section 4.5.10 of ITU Q.931
 
   Last but not least a (at the moment fairly primitive) device to request
   the line-status (/dev/isdninfo) is made available.
@@ -243,16 +263,11 @@
 
 2 System prerequisites:
 
-  ATTENTION! The program "insmod" from the Package "modules-1.2.8" (It's
-             on nearly all newer distributions) has a bug, which makes
-             it impossible to set both driver-Id's when loading the
-             icn-module for the Double-ICN-Card. A patch is supplied
-             in the utility-package called "insmod-1.2.8.patch". Change into
-             the source-directory of insmod, and type
-             "patch < insmod-1.2.8.patch". Then recompile it. This will fix
-             the bug.
-             This bug does NOT occur when using insmod with the Teles-driver
-             or a single ICN-card.
+  ATTENTION!
+
+  Always use the latest module utilities. The current version is
+  named in Documentation/Changes. Some old versions of insmod
+  are not capable of setting the driver-Ids correctly.
  
 3. Lowlevel-driver configuration.
 
diff -u --recursive --new-file v2.1.26/linux/Documentation/isdn/README.HiSax linux/Documentation/isdn/README.HiSax
--- v2.1.26/linux/Documentation/isdn/README.HiSax	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.HiSax	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,343 @@
+HiSax is a Linux hardware level driver for passive ISDN cards with Siemens
+chipset (ISAC_S 2085/2086/2186 HSCX SAB 82525). It is based on the Teles
+driver from Jan den Ouden.
+It is meant to be used with isdn4linux, an ISDN Link-level module for Linux 
+written by Fritz Elfert.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+Supported Cards
+---------------
+
+Teles 8.0/16.0/16.3 and compatible
+Teles S0/PCMCIA
+Creatix PnP S0 
+AVM A1 (Fritz)
+ELSA Microlink PCC-16 PCF PCF-Pro PCC-8
+ELSA Quickstep 1000
+ITK ix1-micro Rev.2
+
+Note: PCF PCF-Pro only the ISDN part is supported yet
+      PCC-8 not tested yet
+      Teles PCMCIA is EXPERIMENTAL
+
+If you know other passive cards with the Siemens chipset, please let me know.
+To use the PNP cards you need the isapnptools.
+
+
+Configuring the Driver
+----------------------
+
+The driver can be build direct into the kernel or as module.
+The HiSax driver can be configured using the command line feature
+while loading the kernel with LILO or LOADLIN or, if built as a module,
+using insmod/modprobe with parameters.
+Please note: All PnP cards need config with isapnp and will work only as
+module.
+
+As Module
+---------
+
+insmod/modprobe  hisax.o \
+  io=iobase irq=IRQ mem=membase type=card_type \
+  protocol=D_channel_protocol id=idstring
+
+or, if several cards are installed:
+
+insmod/modprobe hisax.o \
+  io=iobase1,iobase2,... mem=membase1,membase2,... \
+  type=card_type1,card_type2,... \
+  protocol=D_channel_protocol1,D_channel_protocol2,... \
+  id=idstring1%idstring2 ...
+
+where iobaseN = io base address of the Nth card, membaseN = memory
+base address of the Nth card, etc.
+The reason for the delimiter "%" in the idstrings is that "," won't
+work with the current modules package. 
+
+The parameters can be specified in any order. For example, the io
+parameter can precede the irq parameter, or vice versa. If several
+cards are installed the ordering within the comma separated parameter
+lists must be consistent, of course.
+
+Only parameters applicable to the card type need be specified. For
+example, the Teles 16.3 card is not memory mapped, so the mem
+parameter may be omitted for a Teles 16.3. Sometimes it is necessary
+to specify a dummy parameter, however. This is the case when there is
+a card of a different type later in the list that needs a parameter
+which the preceding card does not. For instance, if a Teles 16.0 card
+is listed after a Teles 16.3 card, a dummy mem=0 parameter must be
+specified for the 16.3. Instead of a dummy value, the parameter can
+also be skipped by simply omitting the value. For example:
+mem=,0xd0000. See example 6 below.
+The protocol parameter for the D-channel may be omittedprotocol may be 
+omitted, if you select the correct one during kernel config.
+Valid values are 1 for german 1TR6, 2 for DSS1 (EURO) and 3 for leased lines
+(no D-channel).
+
+The Creatix/Teles PnP cards uses io1= and io2= instead of io= for specifying 
+the I/O addresses of the ISAC and HSCX chips, respectively.
+
+Card types:
+
+    Type                Required parameters (in addition to type and protocol)
+
+    1   Teles 16.0               irq, mem, io
+    2   Teles  8.0               irq, mem
+    3   Teles 16.3 (non PnP)     irq, io
+    4   Creatix/Teles PnP        irq, io0 (ISAC), io1 (HSCX)
+    5   AVM A1 (Fritz)           irq, io
+    6   ELSA PCC/PCF cards       io or nothing for autodetect (the iobase is
+                                 only required, if you have more than one ELSA
+                                 card in your PC)
+    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.
+
+Examples for module loading
+
+1. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 10
+   modprobe hisax type=3 protocol=2 io=0x280 irq=10
+
+2. Teles 16.0, 1TR6 ISDN, I/O base d80 hex, IRQ 5, Memory d0000 hex
+   modprobe hisax protocol=1 type=1 io=0xd80 mem=0xd0000 irq=5
+
+3. Fritzcard, Euro ISDN, I/O base 340 hex, IRQ 10 and ELSA PCF, Euro ISDN
+   modprobe hisax type=5,6 protocol=2,2 io=0x340 irq=10 id=Fritz%Elsa
+
+4. Any ELSA  PCC/PCF card  Euro ISDN
+   modprobe hisax type=6 protocol=2
+
+5. Teles 16.3 PnP, Euro ISDN, with isapnp configured 
+   isapnp config:  (INT 0 (IRQ 10 (MODE +E)))
+ 		   (IO 0 (BASE 0x0580))
+                   (IO 1 (BASE 0x0180))
+   modprobe hisax type=4 protocol=2 io0=0x580 io1=0x180
+
+6. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 12 and
+   Teles 16.0, 1TR6, IRQ 5, Memory d0000 hex
+   modprobe hisax type=3,1 protocol=2,1 io=0x280 mem=0,0xd0000
+
+   Please note the dummy 0 memory address for the Teles 16.3 in the last 
+   example as placeholder.
+
+7. Teles PCMCIA EURO 180 hex IRQ 15 (default values)
+   modprobe hisax type=8 protocol=2 io=0x180 irq=15
+
+
+LILO/LOADLIN with driver compiled direct into the kernel:
+
+hisax=typ1,dp1,pa_1,pb_1,pc_1[,typ2,dp2,pa_2 ... \
+      typn,dpn,pa_n,pb_n,pc_n][,idstring1[,idstring2,...,idstringn]]
+
+where
+     typ1 = Cardtype of 1st card. (default: depends on kernel settings)
+     dp1  = D-channel protocol of 1st card. 1=1TR6, 2=EDSS1, 3=leased
+     pa_1 = 1. parameter (depend on the cardtype)
+     pb_1 = 2. parameter (depend on the cardtype)
+     pc_1 = 3. parameter (depend on the cardtype)
+
+     typ2,dp2,pa_2,pb_2,pc_2 = Parameters of second card   (defaults: none)
+     typn,dpn,pa_n,pb_n,pc_n = Parameters of n'th card (up to 16 cards are supported)
+
+     idstring = Driver-Id for accessing with utilities and identification
+                when using a Line-monitor. (default: HiSax)
+                idstring must start with a character!
+
+Card types:
+       
+   typ  
+    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 WORK AS MODULE !
+    5 	AVM A1 (Fritz)  	pa=irq	pb=iobase
+    6 	ELSA PCC/PCF cards      pa=iobase or nothing for autodetect 
+    7   ELSA Quickstep 1000	ONLY WORK AS MODULE !
+    8   Teles S0 PCMCIA    	pa=irq  pb=iobase
+    9   ITK ix1-micro Rev.2     pa=irq  pb=iobase
+
+
+Running
+-------
+
+When you insmod isdn.o and hisax.o (or with the kernel-version, during 
+boottime) a few lines should appear in your syslog. Look for something like:
+
+Oct 13 20:46:39 kke01 kernel: ISDN subsystem Rev: 1.23/1.21/1.18/1.13/none loaded
+Oct 13 20:46:41 kke01 kernel: HiSax: Driver for Siemens Chipset ISDN cards
+Oct 13 20:46:41 kke01 kernel: HiSax: Revision (1.3)
+Oct 13 20:46:41 kke01 kernel: HiSax: Total 1 card defined
+Oct 13 20:46:41 kke01 kernel: HiSax: Card 1 Protocol EDSS1
+...
+Oct 13 20:46:41 kke01 kernel: Elsa: PCC16 found at 0x360 Rev.:B IRQ 9
+...
+Oct 13 20:46:41 kke01 kernel: HiSax: 2 channels available
+Oct 13 20:46:41 kke01 kernel: HiSax: module installed
+
+This means that the card is ready for use.
+Cabling problems or line downs are not detected, ELSA 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-commandline. This
+string MUST NOT start with a digit or a small 'x'!
+
+At this point you can run a 'cat /dev/isdnctrl0' and view debugging 
+messages. 
+In the moment debugging messages are enabled with the telesctrl tool : 
+
+    telesctrl <DriverId> DebugCmd <debugging_flags>  
+
+<DriverId> default is HiSax, if you didn't specified one.
+
+DebugCmd is 1   for generic Debug
+            11  for Layer 1 development Debug
+            13  for Layer 3 development Debug
+
+where <debugging_flags> is the integer sum of the following debugging
+options you wish enabled:
+
+With DebugCmd 1 is
+
+        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 debug
+       256  B-Channel link state debug
+       512  Tei debug
+      1024  LOCK debug in callc.c
+
+With DebugCmd 11 is
+
+         1  Warnings (default on)
+         2  Irq Status
+         4  Isac
+         8  Isac fifo
+        16  Hscx
+        32  Hscx fifo (attention full B-channel output)
+        64  D-channel LAPD frame types
+
+With DebugCmd 13 is
+
+         1  Warnings (default on)
+         2  l3 protocol discriptor errors
+         4  l3 stat machine
+         8  charge info debug (1TR6)
+
+For example 'telesctrl HiSax 1 0x3ff' enables full generic debugging.
+
+
+
+Warning
+-------
+HiSax is a work in progress and may crash your machine. It has not been
+certified and therefore operation on your PTT's ISDN network is probably
+illegal.
+
+Limitations
+-----------
+HiSax only works on Euro ISDN lines and german 1TR6-lines.(in this time)
+
+Bugs 
+----
+If you find any please let me know. 
+
+Thanks
+------
+Special thanks to:
+
+        Emil Stephan for the name HiSax which is a mix of HSCX and ISAC.
+
+        Fritz Elfert, Jan den Ouden, Michael Hipp, Michael Wein, 
+        Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en,
+	Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (Elsa GmbH),
+	Volker Schmidt
+        and more people, who are hunting bugs (If I forgot somebody, please
+	send me a mail). 
+
+        Firma ELSA GmbH
+        
+        My girl friend and partner in life Ute for her patience with me
+
+
+Enjoy,
+
+Karsten Keil   
+keil@temic-ech.spacenet.de
+
+Appendix: PCMCIA driver
+-----------------------
+
+Up to now, the Teles PCMCIA driver is a complete hack. I had no
+documentation about the card - it might destroy your hardware (though mine
+at last works fine).
+
+Second I'm working on a module for linux pcmcia services, this version
+clearly assumes some (unrealistic) facts about your hardware:
+  - you MUST have an i82365
+  - Teles PCMCIA MUST be in slot 0 (the upper one) and the ONLY card
+    (since you can't use cardmgr, there's no use for a second one)
+  - ports 0x180-0x1e0 must be free, it uses IRQ 15
+
+The following program will initialize the PCMCIA chip to the values I probed
+inside Windows95 (native Teles driver).
+
+------------------telesinit.c-----------------------------------------
+/* telesinit.c, compile with
+ *   gcc -O -o telesinit telesinit.c
+ */
+#include <asm/io.h>
+#define inportb(x) inb(x)
+#define outportb(x,v) outb(v,x)
+#define BASE 0x180
+#define NUM 97
+#define CARD 0x3e0
+#define i365(x) ( outportb(CARD,x), inportb(CARD+1) )
+#define i365w(x,y) ( outportb(CARD,x), outportb(CARD+1,y) )
+
+void main(int argc,char **argv)
+{  int i;
+   static unsigned char i365_cont[]=
+   { 0x83,0x7f,0xf5,0x6f, 0x00,0x08,0x60,0x00, 
+     0x80,0x01,0xe0,0x01, 0xff,0xff,0xff,0xff,
+     0xd0,0x00,0xd0,0x00, 0x30,0x7f,0x00,0x0f,
+     0xff,0x07,0xff,0x07, 0x00,0x00,0x00,0xff
+   };
+
+   ioperm(BASE,NUM,1); ioperm(CARD,2,1);
+   for (i=0;i<32;i++)
+   {  if (i365_cont[i]==i365(i)) continue;
+      outportb(CARD+1,i365_cont[i]);
+   }
+
+   outportb(BASE+0x18,0x41); 
+   /* enable the teles card and its IRQ */
+   /* this was found by brute force */
+}
+------------------------------------------------------------------------
+
+If you want to wait for the PCMCIA services driver ... I finish my diploma
+about end of February 1997 ... please be patient with me 
+    Christof Petig (ea0141@uni-wuppertal.de)
diff -u --recursive --new-file v2.1.26/linux/Documentation/isdn/README.audio linux/Documentation/isdn/README.audio
--- v2.1.26/linux/Documentation/isdn/README.audio	Fri Jun  7 06:02:39 1996
+++ linux/Documentation/isdn/README.audio	Tue Feb 25 17:12:49 1997
@@ -1,4 +1,4 @@
-$Id: README.audio,v 1.3 1996/06/05 02:19:36 fritz Exp $
+$Id: README.audio,v 1.4 1997/02/03 23:43:47 fritz Exp $
 
 ISDN subsystem for Linux.
   Description of audio mode.
@@ -86,8 +86,11 @@
     starts sending audio data to the application. There are several
     escape sequences defined, all using DLE (0x10) as Escape char:
 
-    <DLE><ETX>              End of audio data. Emulator stops
+    <DLE><ETX>              End of audio data. (i.e. caused by a
+                            hangup of the remote side) Emulator stops
                             recording, responding with VCON.
+	<DLE><DC4>				Abort recording, (send by appl.) Emulator
+							stops recording, sends DLE,ETX.
     <DLE><DLE>              Escape sequence for DLE in data stream.
     <DLE>0                  Touchtone "0" received.
          ...
@@ -107,13 +110,16 @@
     <DLE>s                  silence. Silence detected from the
                             start of recording.
 
-    Any character sent by the application, except XON (0x11) or XOFF (0x13)
-    immediately stops recording.
-
   Audio playback.
 
     When sending audio data, upon AT+VTX command, emulator responds with
     CONNECT, and starts transferring data from application to the phone line.
     The same DLE sequences apply to this mode.
 
+  Full-Duplex-Audio:
+
+    When _both_ commands for recording and playback are given in _one_
+    AT-command-line (i.e.: "AT+VTX+VRX"), full-duplex-mode is selected.
+	In this mode, the only way to stop recording is sending <DLE><DC4>
+    and the only way to stop playback is to send <DLE><ETX>.
 
diff -u --recursive --new-file v2.1.26/linux/Documentation/isdn/README.sc linux/Documentation/isdn/README.sc
--- v2.1.26/linux/Documentation/isdn/README.sc	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/isdn/README.sc	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,274 @@
+Welcome to Beta Release 2 of the combination ISDN driver for SpellCaster's
+ISA ISDN adapters. Please note this release 2 includes support for the
+DataCommute/BRI and TeleCommute/BRI adapters only and any other use is 
+guaranteed to fail. If you have a DataCommute/PRI installed in the test
+computer, we recommend removing it as it will be detected but will not
+be usable.  To see what we have done to Beta Release 2, see section 3.
+
+Speaking of guarantees, THIS IS BETA SOFTWARE and as such contains
+bugs and defects either known or unknown. Use this software at your own
+risk. There is NO SUPPORT for this software. Some help may be available
+through the web site or the mailing list but such support is totally at
+our own option and without warrantee. If you choose to assume all and
+total risk by using this driver, we encourage you to join the beta
+mailing list.
+
+To join the Linux beta mailing list, send a message to:
+majordomo@spellcast.com with the words "subscribe linux-beta" as the only
+contents of the message. Do not include a signature. If you choose to
+remove yourself from this list at a later date, send another message to
+the same address with the words "unsubscribe linux-beta" as it's only
+contents.
+
+TABLE OF CONTENTS
+-----------------
+	1. Introduction
+	 1.1 What is ISDN4Linux?
+	 1.2 What is different between this driver and previous drivers?
+	 1.3 How do I setup my system with the correct software to use
+	     this driver release?
+	
+	2. Basic Operations
+	 2.1 Unpacking and installing the driver
+	 2.2 Read the man pages!!!
+	 2.3 Installing the driver
+	 2.4 Removing the driver
+	 2.5 What to do if it doesn't load
+	 2.6 How to setup ISDN4Linux with the driver
+
+	3. Beta Change Summaries and Miscellaneous Notes
+
+1. Introduction
+---------------
+
+The revision 2 Linux driver for SpellCaster ISA ISDN adapters is built
+upon ISDN4Linux available seperately or as included in Linux 2.0 and later.
+The driver will support a maximum of 4 adapters in any one system of any
+type including DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI for a
+maximum of 92 channels for host. The driver is supplied as a module in
+source form and needs to be complied before it can be used. It has been
+tested on Linux 2.0.20.
+
+1.1 What Is ISDN4Linux
+
+ISDN4Linux is a driver and set of tools used to access and use ISDN devices
+on a Linux platform in a common and standard way. It supports HDLC and PPP
+protocols and offers channel bundling and MLPPP support. To use ISDN4Linux
+you need to configure your kernel for ISDN support and get the ISDN4Linux
+tool kit from our web site.
+
+ISDN4Linux creates a channel pool from all of the available ISDN channels
+and therefore can function across adapters. When an ISDN4Linux compliant
+driver (such as ours) is loaded, all of the channels go into a pool and
+are used on a first-come first-served basis. In addition, individual
+channels can be specifically bound to particular interfaces.
+
+1.2 What is different between this driver and previous drivers?
+
+The revision 2 driver besides adopting the ISDN4Linux architecture has many
+subtle and not so subtle functional differences from previous releases. These
+include:
+	- More efficient shared memory management combined with a simpler
+	  configuration. All adapters now use only 16Kbytes of shared RAM
+	  versus between 16K and 64K. New methods for using the shared RAM
+	  allow us to utilize all of the available RAM on the adapter through
+	  only one 16K page.
+	- Better detection of available upper memory. The probing routines
+	  have been improved to better detect avaialble shared RAM pages and
+	  used pages are now locked.
+	- Decreased loading time and a wider range of I/O ports probed.
+	  We have significantly reduced the amount of time it takes to load
+	  the driver and at the same time doubled the number of I/O ports
+	  probed increasing the likelyhood of finding an adapter.
+	- We now support all ISA adapter models with a single driver instead
+	  of seperate drivers for each model. The revision 2 driver supports
+	  the DataCommute/BRI, DataCommute/PRI and TeleCommute/BRI in any
+	  combination up to a maximum of four adapters per system.
+	- On board PPP protocol support has been removed in favour of the
+	  sync-PPP support used in ISDN4Linux. This means more control of
+	  the protocol parameters, faster negotiation time and a more
+	  familiar interface.
+
+1.3 How do I setup my system with the correct software to use
+    this driver release?
+
+Before you can compile, install and use the SpellCaster ISA ISDN driver, you
+must ensure that the following software is installed, configuraed and running:
+
+	- Linux kernel 2.0.20 or later with the required init and ps
+	  versions. Please see your distribution vendor for the correct
+	  utility packages. The latest kernel is available from
+	  ftp://sunsite.unc.edu/pub/Linux/kernel/v2.0/
+
+	- The latest modules package (modules-2.0.0.tar.gz) from
+	  ftp://sunsite.unc.edu/pub/Linux/kernel/modules-2.0.0.tar.gz
+
+	- The ISDN4Linux tools available from 
+	  ftp://ftp.franken.de/pub/isdn4linux/v2.0/isdn4k-utils-2.0.tar.gz
+	  This package may fail to compile for you so you can alternatively
+	  get a pre-compiled version from
+	  ftp://ftp.spellcast.com/pub/drivers/isdn4linux/isdn4k-bin-2.0.tar.gz
+
+
+2. Basic Operations
+-------------------
+
+2.1 Unpacking and installing the driver
+
+	1. As root, create a directory in a convienient place. We suggest
+	   /usr/src/spellcaster.
+	
+	2. Unpack the archive with :
+		tar xzf sc-n.nn.tar.gz -C /usr/src/spellcaster
+	
+	3. Change directory to /usr/src/spellcaster
+
+	4. Read the README and RELNOTES files.
+
+	5. Run 'make' and if all goes well, run 'make install'.
+
+2.2 Read the man pages!!!
+
+Make sure you read the scctrl(8) and sc(4) manual pages before continuing
+any further. Type 'man 8 scctrl' and 'man 4 sc'.
+
+2.3 Installing the driver
+
+To install the driver, type '/sbin/insmod sc' as root. sc(4) details options
+you can specify but you shouldn't need to use any unless this doesn't work.
+
+Make sure the driver loaded and detected all of the adapters by typing
+'dmesg'.
+
+The driver can be configured so that it is loaded upon startup.  To do this, 
+edit the file "/etc/modules/'uname -f'/'uname -v'" and insert the driver name
+"sc" into this file.
+
+2.4 Removing the driver
+
+To remove the driver, delete any interfaces that may exist (see isdnctrl(8)
+for more on this) and then type '/sbin/rmmod sc'.
+
+2.5 What to do if it doesn't load
+
+If, when you try to install the driver, you get a message mentioning
+'register_isdn' then you do not have the ISDN4Linux system installed. Please
+make sure that ISDN support is configured in the kernel.
+
+If you get a message that says 'initialization of sc failed', then the
+driver failed to detect an adapter or failed to find resources needed such
+as a free IRQ line or shared memory segment. If you are sure there are free
+resources available, use the insmod options detailed in sc(4) to override
+the probing function.  
+
+Upon testing, the following problem was noted, the driver would load without
+problems, but the board would not respond beyond that point.  When a check was 
+done with 'cat /proc/interrupts' the interrupt count for sc was 0.  In the event 
+of this problem, change the BIOS settings so that the interrupts in question are
+reserved for ISA use only.   
+
+
+2.6 How to setup ISDN4Linux with the driver
+
+There are two main configurations which you can use with the driver:
+
+A)	Basic HDLC connection
+B)	PPP connection
+C)	MLPPP connection
+
+It should be mentioned here that you may also use a tty connection if you desire. 
+The Documentation directory of the isdn4linux subsystem offers a good documentation
+on this feature.
+
+A) 10 steps to the establishment of a basic HDLC connection
+-----------------------------------------------------------
+
+- please open the isdn-hdlc file in the examples directory and follow along...
+	
+	This file is a script used to configure a BRI ISDN TA to establish a basic HDLC
+	connection between its two channels.  There two network interfaces which are 
+	created and two routes added between the channels.
+
+	i)   using the isdnctrl utitity, add an interface with "addif" and name it "isdn0"
+	ii)  add the outgoing and inbound telephone numbers
+	iii) set the Layer 2 protocol to hdlc
+	iv)  set the eaz of the interface to be the phone number of that specific channel
+	v)   to turn the callback features off, set the callback to "off" and
+	     the callback delay (cbdelay) to 0.
+	vi)  the hangup timeout can be set to a specified number of seconds
+	vii) the hangup upon incomming call can be set on or off 
+	viii) use the ifconfig command to bring-up the network interface with a specific
+	     IP address and point to point address
+	viv) add a route to the IP address through the isdn0 interface
+	x)   a ping should result in the establishment of the connection
+
+	
+B) Establishment of a PPP connection
+------------------------------------
+
+- please open the isdn-ppp file in the examples directory and follow along...
+	
+	This file is a script used to configure a BRI ISDN TA to establish a PPP connection 
+	between the two channels.  The file is almost identical to the HDLC connection
+	example except that the packet ecapsulation type has to be set.
+	
+	use the same procedure as in the HDLC connection from steps i) to iii) then, 
+	after the Layer 2 protocol is set, set the encapsulation  "encap" to syncppp.
+	With this done, the rest of the steps, iv) to x) can be followed from above.
+
+	Then, the ipppd (ippp daemon) must be setup:
+	
+	xi)   use the ipppd function found in /sbin/ipppd to set the following:
+	xii)  take out (minus) VJ compression and bsd compression
+	xiii) set the mru size to 2000
+	xiv)  link the two /dev interfaces to the daemon
+
+NOTE:  A "*" in the inbound telephone number specifies that a call can be accepted
+       on any number.
+
+C) Establishment of a MLPPP connection
+--------------------------------------
+
+- please open the isdn-mppp file in the examples directory and follow along...
+	
+	This file is a script used to configure a BRI ISDN TA to accept a Multi Link PPP
+	connection. 
+	
+	i)   using the isdnctrl utitity, add an interface with "addif" and name it "ippp0"
+	ii)  add the inbound telephone number
+	iii) set the Layer 2 protocol to hdlc and the Layer 3 protocol to trans (transparent)
+	iv)  set the packet encapsulation to syncppp
+	v)   set the eaz of the interface to be the phone number of that specific channel
+	vi)   to turn the callback features off, set the callback to "off" and
+	     the callback delay (cbdelay) to 0.
+	vi)  the hangup timeout can be set to a specified number of seconds
+	vii) the hangup upon incomming call can be set on or off 
+	viii) add a slave interface and name it "ippp32" for example
+	viv)  set the similar parameters for the ippp32 interface
+	x)    use the ifconfig command to bring-up the ippp0 interface with a specific
+	     IP address and point to point address
+	xi)  add a route to the IP address through the ippp0 interface
+	xii) use the ipppd function found in /sbin/ipppd to set the following:
+	xiii) take out (minus) bsd compression
+	xiv) set the mru size to 2000
+	xv)  add (+) the multi-link function "+mp"
+	xv)  link the two /dev interfaces to the daemon
+
+NOTE:  To use the MLPPP connection to dial OUT to a MLPPP connection, change the 
+       inbound telephone numbers to the outgoing telephone numbers of the MLPPP 
+       host.
+
+	
+3. Beta Change Summaries and Miscellaneous Notes
+------------------------------------------------
+When using the "scctrl" utility to upload firmware revisions on the board, please
+note that the byte count displayed at the end of the operation may be different 
+than the total number of bytes in the "dcbfwn.nn.sr" file.  Please disregard the 
+displayed byte count.
+
+It was noted that in Beta Release 1, the module would fail to load and result in a
+segmentation fault when insmod"ed".  This problem was created when one of the 
+isdn4linux parameters, (isdn_ctrl, data field) was filled in.  In some cases, this
+data field was NULL, and was left unchecked, so when it was referenced.. segv.  
+The bug has been fixed around line 63-68 of event.c.
+
diff -u --recursive --new-file v2.1.26/linux/Documentation/networking/ax25.txt linux/Documentation/networking/ax25.txt
--- v2.1.26/linux/Documentation/networking/ax25.txt	Sun Feb  2 05:18:29 1997
+++ linux/Documentation/networking/ax25.txt	Fri Feb 21 14:58:32 1997
@@ -1,53 +1,14 @@
-With the version of the AX.25, NET/ROM and Rose protocol stacks provided in
-the Linux kernel from 2.1.9 onwards, a change has occurred in the
-configuration of the protocols. With previous versions such changes were
-made via ioctl calls, but now use is being made of the sysctl interface.
-
-Each AX.25 device will be represented in the directory /proc/sys/net/ax25,
-in the form "dev.parms"  where dev is the device name, eg ax0. In it are a
-string of numbers that represent different values for the different
-parameters, they are: 
-
-No.	Name			Meaning				Default
-1	IP Default Mode		0=DG 1=VC			0
-2	AX.25 Default Mode	0=Normal 1=Extended		0
-3	Allow Vanilla Connects	0=No 1=Yes			1
-4	Backoff			0=None 1=Linear 2=Exponential	1
-5	Connected Mode		0=No 1=Yes			1
-6	Standard Window		1  <= N <= 7			2
-7	Extended Window		1  <= N <= 63			32
-8	T1 Timeout		1s <= N <= 30s			10s
-9	T2 Timeout		1s <= N <= 20s			3s
-10	T3 Timeout		0s <= N <= 3600s		300s
-11	Idle Timeout		0m <= N				20m
-12	N2			1  <= N <= 31			10
-13	AX.25 MTU		1  <= N <= 512			256
-14	Max Queue		1  <= N <= 20			2
-15	Digipeater Mode		0=None 1=Inband 2=XBand 3=Both	3
-
-In the above list T1, T2 and T3 are given in seconds, and the Idle Timeout
-is given in minutes. But please note that the values used in the sysctl
-interface are given in internal units where the time in seconds is
-multiplied by 10, this allows resolution down to 1/10 of a second. With
-timers that are allowed to be zero, eg T3 and Idle, a zero value indicates
-that the timer is disabled. 
-
-With NET/ROM and Rose protocol stacks, the entries in /proc/sys/net/netrom
-and /proc/sys/net/rose are more obvious. Each file in these directories has
-a name more in keeping with its function, and will not be explained in any
-greater depth here. As with the AX.25 sysctl entries, timers operate with a
-resolution of 100ms and so values should be written accordingly.
-
-It is possible that the AX.25 sysctl interface will change in the future and
-become more user friendly.
-
-For more information about the AX.25 and NET/ROM protocol stacks, see the
-AX25-HOWTO written by Terry Dawson <terry@perf.no.itg.telstra.com.au> who is
-also the AX.25 Utilities maintainer.
+To use the amateur radio protocols within Linux you will need to get a
+suitable copy of the AX.25 Utilities. More detailed information about these
+and associated programs can be found on http://www.cs.nott.ac.uk/~jsn/.
+
+For more information about the AX.25, NET/ROM and ROSE protocol stacks, see
+the AX25-HOWTO written by Terry Dawson <terry@perf.no.itg.telstra.com.au>
+who is also the AX.25 Utilities maintainer.
 
 There is an active mailing list for discussing Linux amateur radio matters
 called linux-hams. To subscribe to it, send a message to
-majordomo@vger.rutgers.edu with the words "subscribe linux-hams" in the body
+Majordomo@vger.rutgers.edu with the words "subscribe linux-hams" in the body
 of the message, the subject field is ignored.
 
 Jonathan G4KLX
diff -u --recursive --new-file v2.1.26/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.26/linux/MAINTAINERS	Fri Feb  7 05:54:53 1997
+++ linux/MAINTAINERS	Fri Feb 21 14:58:32 1997
@@ -81,6 +81,7 @@
 M	gpg109@rsphy1.anu.edu.au
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
+W:	http://rsphy1.anu.edu.au/~gpg109/ne2000.html
 
 ETHEREXPRESS-16 NETWORK DRIVER
 P:	Philip Blundell
diff -u --recursive --new-file v2.1.26/linux/Makefile linux/Makefile
--- v2.1.26/linux/Makefile	Fri Feb  7 06:30:55 1997
+++ linux/Makefile	Fri Feb 21 15:33:17 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 26
+SUBLEVEL = 27
 
 ARCH = i386
 
@@ -11,7 +11,7 @@
 #
 # NOTE! SMP is experimental. See the file Documentation/SMP.txt
 #
-# SMP = 1
+SMP = 1
 #
 # SMP profiling options
 # SMP_PROF = 1
@@ -330,6 +330,9 @@
 	rm -f drivers/sound/local.h drivers/sound/.defines
 	rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h
 	rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash
+	rm -f drivers/net/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h
+	rm -f drivers/net/soundmodem/sm_tbl_{hapn4800,psk4800}.h
+	rm -f drivers/net/soundmodem/gentbl
 	rm -f .version .config* config.in config.old
 	rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
 	rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
diff -u --recursive --new-file v2.1.26/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.26/linux/arch/alpha/kernel/entry.S	Thu Feb  6 04:42:35 1997
+++ linux/arch/alpha/kernel/entry.S	Wed Feb 26 13:34:09 1997
@@ -123,7 +123,7 @@
 	SAVE_ALL
 /* start atomic operation with respect to software interrupts */
 	lda	$0,intr_count
-	ldq	$1,0($0)
+	ldl	$1,0($0)
 	addq	$1,1,$1
 	stq	$1,0($0)
 /* set up the arguments to the C interrupt handler */
@@ -131,7 +131,7 @@
 	jsr	$26,do_entInt
 /* ok, return */
 	lda	$0,intr_count
-	ldq	$1,0($0)
+	ldl	$1,0($0)
 	subq	$1,1,$1
 	stq	$1,0($0)
 	br	$31,ret_from_sys_call
@@ -527,7 +527,7 @@
 	cmovne	$26,0,$19		/* $19 = 0 => non-restartable */
 	/* check bottom half interrupts */
 	lda	$0,intr_count
-	ldq	$1,0($0)
+	ldl	$1,0($0)
 	bne	$1,ret_from_handle_bh
 	lda	$2,bh_active
 	ldq	$3,0($2)
@@ -622,7 +622,7 @@
 	 * We're called with $0 containing the address of
 	 * 'intr_count' and $1 containing 'intr_count+1'
 	 */
-	stq	$1,0($0)	/* intr_count = 1 */
+	stl	$1,0($0)	/* intr_count = 1 */
 	subq	$30,16,$30
 	stq	$19,0($30)	/* save syscall nr */
 	stq	$20,8($30)	/* and error indication (a3) */
@@ -631,7 +631,7 @@
 	ldq	$19,0($30)
 	ldq	$20,8($30)
 	addq	$30,16,$30
-	stq	$31,0($0)	/* intr_count = 0 */
+	stl	$31,0($0)	/* intr_count = 0 */
 	br	$31,ret_from_handle_bh
 
 	.align 3
diff -u --recursive --new-file v2.1.26/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.26/linux/arch/i386/defconfig	Fri Feb  7 06:49:49 1997
+++ linux/arch/i386/defconfig	Fri Feb 21 15:32:54 1997
@@ -26,8 +26,8 @@
 CONFIG_BINFMT_ELF=y
 # CONFIG_M386 is not set
 # CONFIG_M486 is not set
-CONFIG_M586=y
-# CONFIG_M686 is not set
+# CONFIG_M586 is not set
+CONFIG_M686=y
 # CONFIG_VIDEO_SELECT is not set
 
 #
@@ -48,7 +48,7 @@
 CONFIG_BLK_DEV_CMD640=y
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_RZ1000=y
-# CONFIG_BLK_DEV_TRITON is not set
+CONFIG_BLK_DEV_TRITON=y
 # CONFIG_IDE_CHIPSETS is not set
 
 #
@@ -92,7 +92,51 @@
 #
 # SCSI support
 #
-# CONFIG_SCSI is not set
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+CONFIG_SCSI_BUSLOGIC=y
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+# CONFIG_SCSI_PPA is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
 
 #
 # Network device support
@@ -102,15 +146,16 @@
 CONFIG_DUMMY=m
 # CONFIG_EQUALIZER is not set
 CONFIG_NET_ETHERNET=y
-CONFIG_NET_VENDOR_3COM=y
-# CONFIG_EL1 is not set
-# CONFIG_EL2 is not set
-CONFIG_EL3=y
-# CONFIG_VORTEX is not set
+# CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_ISA is not set
-# CONFIG_NET_EISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_DE4X5=y
+# CONFIG_DEC_ELCP is not set
+# CONFIG_DGRS is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
@@ -139,7 +184,7 @@
 CONFIG_EXT2_FS=y
 CONFIG_FAT_FS=y
 CONFIG_MSDOS_FS=y
-# CONFIG_VFAT_FS is not set
+CONFIG_VFAT_FS=y
 # CONFIG_UMSDOS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
@@ -162,7 +207,12 @@
 # CONFIG_RISCOM8 is not set
 # CONFIG_ESPSERIAL is not set
 # CONFIG_PRINTER is not set
-# CONFIG_MOUSE is not set
+CONFIG_MOUSE=y
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+CONFIG_82C710_MOUSE=y
 # CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_FTAPE is not set
diff -u --recursive --new-file v2.1.26/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.26/linux/arch/i386/kernel/entry.S	Sun Jan 26 02:07:04 1997
+++ linux/arch/i386/kernel/entry.S	Wed Feb 26 13:22:32 1997
@@ -163,7 +163,7 @@
 
 	ALIGN
 handle_bottom_half:
-	pushl $9f
+	pushl $2f
 	jmp SYMBOL_NAME(do_bottom_half)
 
 	ALIGN
@@ -183,14 +183,20 @@
 	movl %eax,EAX(%esp)		# save the return value
 	ALIGN
 	.globl ret_from_sys_call
+	.globl ret_from_intr
+ret_from_intr:
 ret_from_sys_call:
 	GET_CURRENT(%ebx)
 	cmpl $0,SYMBOL_NAME(intr_count)
+#ifdef __SMP__
+	jne 2f
+#else
 	jne 1f
+#endif
 9:	movl SYMBOL_NAME(bh_mask),%eax
 	andl SYMBOL_NAME(bh_active),%eax
 	jne handle_bottom_half
-	movl EFLAGS(%esp),%eax		# mix EFLAGS and CS
+2:	movl EFLAGS(%esp),%eax		# mix EFLAGS and CS
 	movb CS(%esp),%al
 	testl $(VM_MASK | 3),%eax	# return to VM86 mode or non-supervisor?
 	je 1f
@@ -201,7 +207,8 @@
 	notl %eax
 	andl signal(%ebx),%eax
 	jne signal_return
-1:	RESTORE_ALL
+1:	
+	RESTORE_ALL
 	ALIGN
 signal_return:
 	testl $(VM_MASK),EFLAGS(%esp)
diff -u --recursive --new-file v2.1.26/linux/arch/i386/kernel/sys_i386.c linux/arch/i386/kernel/sys_i386.c
--- v2.1.26/linux/arch/i386/kernel/sys_i386.c	Fri Feb  7 05:54:53 1997
+++ linux/arch/i386/kernel/sys_i386.c	Tue Feb 25 13:36:08 1997
@@ -88,14 +88,11 @@
 asmlinkage int old_select(struct sel_arg_struct *arg)
 {
 	struct sel_arg_struct a;
-	int ret;
 
 	if (copy_from_user(&a, arg, sizeof(a)))
 		return -EFAULT;
-	lock_kernel();
-	ret = sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
-	unlock_kernel();
-	return ret;
+	/* sys_select() does the appropriate kernel locking */
+	return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.1.26/linux/arch/i386/kernel/time.c	Fri Feb  7 05:54:54 1997
+++ linux/arch/i386/kernel/time.c	Fri Feb 21 14:58:32 1997
@@ -33,6 +33,10 @@
 #include <linux/config.h>
 
 extern int setup_x86_irq(int, struct irqaction *);
+extern volatile unsigned long lost_ticks;
+
+/* change this if you have some constant time drift */
+#define USECS_PER_JIFFY (1000020/HZ)
 
 #ifndef	CONFIG_APM	/* cycle counter may be unreliable */
 /* Cycle counter value at the previous timer interrupt.. */
@@ -40,11 +44,6 @@
 	unsigned long low;
 	unsigned long high;
 } init_timer_cc, last_timer_cc;
-
-extern volatile unsigned long lost_ticks;
-
-/* change this if you have some constant time drift */
-#define USECS_PER_JIFFY (1000020/HZ)
 
 static unsigned long do_fast_gettimeoffset(void)
 {
diff -u --recursive --new-file v2.1.26/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- v2.1.26/linux/arch/i386/kernel/vm86.c	Sun Feb  2 05:18:30 1997
+++ linux/arch/i386/kernel/vm86.c	Tue Feb 25 12:19:18 1997
@@ -434,10 +434,10 @@
 		if ( (trapno==3) || (trapno==1) )
 			return_to_32bit(regs, VM86_TRAP + (trapno << 8));
 		do_int(regs, trapno, (unsigned char *) (regs->ss << 4), SP(regs));
-		return  1;
+		return 0;
 	}
 	if (trapno !=1)
-		return 0; /* we let this handle by the calling routine */
+		return 1; /* we let this handle by the calling routine */
 	if (current->flags & PF_PTRACED)
 		current->blocked &= ~(1 << (SIGTRAP-1));
 	send_sig(SIGTRAP, current, 1);
diff -u --recursive --new-file v2.1.26/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.26/linux/drivers/block/ll_rw_blk.c	Mon Dec 23 00:13:53 1996
+++ linux/drivers/block/ll_rw_blk.c	Wed Feb 26 11:10:36 1997
@@ -585,7 +585,7 @@
 		for (; j < 8 && i < nb; j++, i++, buf += buffersize)
 		{
 		        rdev = dev;
-			rsector = (b[i] * buffersize) >> 9;
+			rsector = b[i] * (buffersize >> 9);
 #ifdef CONFIG_BLK_DEV_MD
 			if (major==MD_MAJOR &&
 			    md_map (MINOR(dev), &rdev,
diff -u --recursive --new-file v2.1.26/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.26/linux/drivers/char/lp.c	Sun Jan 26 02:07:16 1997
+++ linux/drivers/char/lp.c	Fri Feb 21 14:58:33 1997
@@ -8,6 +8,7 @@
  * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
  * Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
  * "lp=" command line parameters added by Grant Guenther, grant@torque.net
+ * lp_read (Status readback) support added by Carsten Gross, carsten@sol.wohnheim.uni-ulm.de
  */
 
 #include <linux/module.h>
@@ -316,6 +317,116 @@
 	return -ESPIPE;
 }
 
+/* Test if nibble mode for status readback is okay. 
+ * returns false if not, otherwise true
+ */
+static int lp_nibble_mode_ok(int minor) 
+{
+	int reply=1;
+
+	outb_p(0, LP_B(minor)); 	/* Request "nibble mode" */
+	udelay(LP_DELAY);		
+	outb((inb(LP_C(minor)) & ~8), LP_C(minor)); /* SelectIN low */
+	outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
+	udelay(LP_DELAY);
+	outb((inb(LP_C(minor)) | 1), LP_C(minor)); /* Strobe high */
+	if (( LP_S(minor) & ~0x80) != 0x38) reply=0; /* expected answer? */
+	outb((inb(LP_C(minor)) & ~1 ), LP_C(minor)); /* Strobe low */
+	outb((inb(LP_C(minor)) & ~2 ), LP_C(minor)); /* Autofeed low */
+	udelay(LP_DELAY);
+	return(reply);
+}
+
+
+static int lp_read_nibble(int minor) 
+{
+	unsigned char i;
+	i=LP_S(minor)>>3;
+	i&=~8;
+	if ( ( i & 0x10) == 0) i|=8;
+	return(i & 0x0f);
+}
+
+static void lp_select_in_high(int minor) {
+	outb((inb(LP_C(minor)) | 8), LP_C(minor));
+}
+
+/* Status readback confirming to ieee1284 */
+static long lp_read(struct inode * inode, struct file * file, 
+		   char * buf, unsigned long count)
+{
+	unsigned char z=0, Byte=0, status;
+	char *temp;
+	int retval;
+	unsigned int counter=0;
+	unsigned int i;
+	unsigned int minor=MINOR(inode->i_rdev);
+	
+	temp=buf;	
+#ifdef LP_READ_DEBUG 
+	printk(KERN_INFO "lp%d: read mode\n", minor);
+#endif
+
+	retval = verify_area(VERIFY_WRITE, buf, count);
+	if (retval)
+		return retval;
+	if (lp_nibble_mode_ok(minor)==0) {
+		lp_select_in_high(minor);
+		return temp-buf;          /*  End of file */
+	}
+	for (i=0; i<=(count*2); i++) {
+		outb((inb(LP_C(minor)) | 2), LP_C(minor)); /* AutoFeed high */
+		do {
+			status=(LP_S(minor) & 0x40);
+			udelay(50);
+			counter++;
+			if (need_resched)
+				schedule();
+		} while ( (status == 0x40) && (counter < 20) );
+		if ( counter == 20 ) { /* Timeout */
+#ifdef LP_READ_DEBUG
+			printk(KERN_DEBUG "lp_read: (Autofeed high) timeout\n");
+#endif		
+			outb((inb(LP_C(minor)) & ~2), LP_C(minor));
+			lp_select_in_high(minor);
+			return temp-buf; /* end the read at timeout */
+		}
+		counter=0;
+		z=lp_read_nibble(minor);
+		outb((inb(LP_C(minor)) & ~2), LP_C(minor)); /* AutoFeed low */
+		do {
+			status=(LP_S(minor) & 0x40);
+			udelay(20);
+			counter++;
+			if (need_resched)
+				schedule();
+		} while ( (status == 0) && (counter < 20) );
+		if (counter == 20) { /* Timeout */
+#ifdef LP_READ_DEBUG
+			printk(KERN_DEBUG "lp_read: (Autofeed low) timeout\n");
+#endif
+			if (current->signal & ~current->blocked) {
+				lp_select_in_high(minor);
+				if (temp !=buf)
+					return temp-buf;
+				else 
+					return -EINTR;
+			}
+			current->state=TASK_INTERRUPTIBLE;
+			current->timeout=jiffies + LP_TIME(minor);
+			schedule();
+		}
+		counter=0;
+		if (( i & 1) != 0) {
+			Byte= (Byte | z<<4);
+			put_user(Byte, temp);
+			temp++;
+		} else Byte=z;
+	}
+	lp_select_in_high(minor);
+	return temp-buf;	
+}
+
 static int lp_open(struct inode * inode, struct file * file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
@@ -524,7 +635,7 @@
 
 static struct file_operations lp_fops = {
 	lp_lseek,
-	NULL,		/* lp_read */
+	lp_read,
 	lp_write,
 	NULL,		/* lp_readdir */
 	NULL,		/* lp_poll */
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/Config.in linux/drivers/isdn/Config.in
--- v2.1.26/linux/drivers/isdn/Config.in	Sun May 19 05:29:29 1996
+++ linux/drivers/isdn/Config.in	Tue Feb 25 17:12:49 1997
@@ -12,3 +12,16 @@
 dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
 dep_tristate 'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
 dep_tristate 'Teles/NICCY1016PC/Creatix support' CONFIG_ISDN_DRV_TELES $CONFIG_ISDN
+dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
+if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
+    bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
+    bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
+    bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
+    bool 'HiSax Support for Elsa cards' CONFIG_HISAX_ELSA_PCC
+    bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
+    bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
+    bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
+fi
+if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+	dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
+fi
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/Makefile linux/drivers/isdn/Makefile
--- v2.1.26/linux/drivers/isdn/Makefile	Sun May 19 05:29:29 1996
+++ linux/drivers/isdn/Makefile	Tue Feb 25 17:12:49 1997
@@ -1,6 +1,6 @@
 SUB_DIRS     :=
 MOD_SUB_DIRS :=
-ALL_SUB_DIRS := icn teles pcbit
+ALL_SUB_DIRS := icn teles pcbit hisax
 
 L_OBJS :=
 LX_OBJS :=
@@ -13,8 +13,8 @@
 
 ifeq ($(CONFIG_ISDN),y)
   L_TARGET := isdn.a
-  L_OBJS += isdn_net.o isdn_tty.o isdn_cards.o
-  LX_OBJS += isdn_common.o
+  L_OBJS += isdn_common.o isdn_net.o isdn_tty.o isdn_cards.o
+  LX_OBJS += isdn_syms.o
   ifdef CONFIG_ISDN_PPP
     L_OBJS += isdn_ppp.o
   endif
@@ -25,8 +25,8 @@
   ifeq ($(CONFIG_ISDN),m)
     M_OBJS += isdn.o
     O_TARGET += isdn.o
-    O_OBJS += isdn_net.o isdn_tty.o
-    OX_OBJS += isdn_common.o
+    O_OBJS += isdn_common.o isdn_net.o isdn_tty.o
+    OX_OBJS += isdn_syms.o
     ifdef CONFIG_ISDN_PPP
       O_OBJS += isdn_ppp.o
     endif
@@ -46,6 +46,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_ISDN_DRV_HISAX),y)
+  L_OBJS += hisax/hisax.o
+  SUB_DIRS += hisax
+  MOD_SUB_DIRS += hisax
+else
+  ifeq ($(CONFIG_ISDN_DRV_HISAX),m)
+    MOD_SUB_DIRS += hisax
+  endif
+endif
+
 ifeq ($(CONFIG_ISDN_DRV_ICN),y)
   L_OBJS += icn/icn.o
   SUB_DIRS += icn
@@ -63,6 +73,16 @@
 else
   ifeq ($(CONFIG_ISDN_DRV_PCBIT),m)
     MOD_SUB_DIRS += pcbit
+  endif
+endif
+
+ifeq ($(CONFIG_ISDN_DRV_SC),y)
+  L_OBJS += sc/sc.o
+  SUB_DIRS += sc
+  MOD_SUB_DIRS += sc
+else
+  ifeq ($(CONFIG_ISDN_DRV_SC),m)
+    MOD_SUB_DIRS += sc
   endif
 endif
 
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/Makefile linux/drivers/isdn/hisax/Makefile
--- v2.1.26/linux/drivers/isdn/hisax/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/Makefile	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,46 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := isdnl1.o config.o buffers.o tei.o isdnl2.o isdnl3.o \
+          q931.o callc.o fsm.o
+
+# EXTRA_CFLAGS += -S
+
+ifeq ($(CONFIG_HISAX_EURO),y)
+	 O_OBJS += l3dss1.o
+endif
+
+ifeq ($(CONFIG_HISAX_1TR6),y)
+	 O_OBJS += l3_1tr6.o
+endif
+
+ifeq ($(CONFIG_HISAX_16_0),y)
+	 O_OBJS += teles0.o
+endif
+
+ifeq ($(CONFIG_HISAX_16_3),y)
+	 O_OBJS += teles3.o
+endif
+
+ifeq ($(CONFIG_HISAX_AVM_A1),y)
+	 O_OBJS += avm_a1.o
+endif
+
+ifeq ($(CONFIG_HISAX_ELSA_PCC),y)
+	 O_OBJS += elsa.o
+endif
+
+ifeq ($(CONFIG_HISAX_IX1MICROR2),y)
+	 O_OBJS += ix1_micro.o
+endif
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_HISAX),y)
+  O_TARGET += hisax.o
+else
+  ifeq ($(CONFIG_ISDN_DRV_HISAX),m)
+    O_TARGET += hisax.o
+    M_OBJS += hisax.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/avm_a1.c linux/drivers/isdn/hisax/avm_a1.c
--- v2.1.26/linux/drivers/isdn/hisax/avm_a1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_a1.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,982 @@
+/* $Id: avm_a1.c,v 1.4 1997/01/27 15:50:21 keil Exp $
+
+ * avm_a1.c     low level stuff for AVM A1 (Fritz) isdn cards
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: avm_a1.c,v $
+ * Revision 1.4  1997/01/27 15:50:21  keil
+ * SMP proof,cosmetics
+ *
+ * Revision 1.3  1997/01/21 22:14:20  keil
+ * cleanups
+ *
+ * Revision 1.2  1996/10/27 22:07:31  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:49  keil
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "avm_a1.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+const char *avm_revision = "$Revision: 1.4 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline byte
+readreg(unsigned int adr, byte off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, byte off, byte data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, byte * data, int size)
+{
+	insb(adr - 0x400, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, byte * data, int size)
+{
+	outsb(adr - 0x400, data, size);
+}
+
+static inline void
+waitforCEC(int adr)
+{
+	int to = 50;
+
+	while ((readreg(adr, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "AVM A1: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr)
+{
+	int to = 50;
+
+	while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "AVM A1: waitforXFW timeout\n");
+}
+static inline void
+writehscxCMDR(int adr, byte data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr);
+	writereg(adr, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR));
+}
+
+void
+avm_a1_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	byte *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh = hsp->rcvibh;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+					      HSCX_RBUF_BPPS)) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->rcvptr;
+
+	hsp->rcvptr += count;
+	save_flags(flags);
+	cli();
+	read_fifo(sp->hscx[hsp->hscx], ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh;
+	int more, count;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	ibh = hsp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - hsp->sendptr;
+	if (count <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->sendptr;
+	hsp->sendptr += count;
+
+	waitforXFW(sp->hscx[hsp->hscx]);
+	save_flags(flags);
+	cli();
+	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, byte val, byte hscx)
+{
+	byte r;
+	struct HscxState *hsp = sp->hs + hscx;
+	int count, err;
+	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");
+			if (hsp->rcvibh)
+				BufPoolRelease(hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+			goto afterRME;
+		}
+		if (!hsp->rcvibh)
+			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+				       GFP_ATOMIC, (void *) 1, 1)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RME out of buffers");
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+				goto afterRME;
+			} else
+				hsp->rcvptr = 0;
+
+		count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		hscx_empty_fifo(hsp, count);
+		hsp->rcvibh->datasize = hsp->rcvptr - 1;
+		BufQueueLink(&hsp->rq, hsp->rcvibh);
+		hsp->rcvibh = NULL;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!hsp->rcvibh) {
+			if (hsp->mode == 1)
+				err = BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+			else
+				err = BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+
+			if (err) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RPF out of buffers");
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+				goto afterRPF;
+			} else
+				hsp->rcvptr = 0;
+		}
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			hsp->rcvibh->datasize = hsp->rcvptr;
+			BufQueueLink(&hsp->rq, hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+      afterRPF:
+	if (val & 0x10) {	/* XPR */
+		if (hsp->xmtibh)
+			if (hsp->xmtibh->datasize > hsp->sendptr) {
+				hscx_fill_fifo(hsp);
+				goto afterXPR;
+			} else {
+				if (hsp->releasebuf)
+					BufPoolRelease(hsp->xmtibh);
+				hsp->sendptr = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->xmtibh = NULL;
+			}
+		if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+			hsp->releasebuf = !0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+      afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	byte *ptr;
+	struct BufHeader *ibh = sp->rcvibh;
+	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->rcvptr >= 3072) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo rcvptr %d", sp->rcvptr);
+			debugl1(sp, tmp);
+		}
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->rcvptr;
+	sp->rcvptr += count;
+
+	save_flags(flags);
+	cli();
+	read_fifo(sp->isac, ptr, count);
+	writereg(sp->isac, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh;
+	int count, more;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	ibh = sp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - sp->sendptr;
+	if (count <= 0)
+		return;
+	if (count >= 3072)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->sendptr;
+	sp->sendptr += count;
+
+	save_flags(flags);
+	cli();
+	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, byte val)
+{
+	byte exval;
+	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");
+			if (sp->rcvibh)
+				BufPoolRelease(sp->rcvibh);
+			sp->rcvibh = NULL;
+			writereg(sp->isac, ISAC_CMDR, 0x80);
+			goto afterRME;
+		}
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 3)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writereg(sp->isac, ISAC_CMDR, 0x80);
+				goto afterRME;
+			} else
+				sp->rcvptr = 0;
+		count = readreg(sp->isac, ISAC_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		isac_empty_fifo(sp, count);
+		sp->rcvibh->datasize = sp->rcvptr;
+		BufQueueLink(&(sp->rq), sp->rcvibh);
+		sp->rcvibh = NULL;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 4)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writereg(sp->isac, ISAC_CMDR, 0x80);
+				goto afterRPF;
+			} else
+				sp->rcvptr = 0;
+		isac_empty_fifo(sp, 32);
+	}
+      afterRPF:
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->xmtibh)
+			if (sp->xmtibh->datasize > sp->sendptr) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				if (sp->releasebuf)
+					BufPoolRelease(sp->xmtibh);
+				sp->xmtibh = NULL;
+				sp->sendptr = 0;
+			}
+		if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+			sp->releasebuf = !0;
+			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, byte val)
+{
+
+	byte 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.
+				 */
+				hsp->sendptr = 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.
+				 */
+				hsp->sendptr = 0;
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readreg(sp->hscx[0], HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	byte val, sval, stat = 0;
+	char tmp[32];
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
+		return;
+	}
+	while (((sval = bytein(sp->cfg_reg)) & 0xf) != 0x7) {
+		if (!(sval & AVM_A1_STAT_TIMER)) {
+			byteout(sp->cfg_reg, 0x14);
+			byteout(sp->cfg_reg, 0x18);
+			sval = bytein(sp->cfg_reg);
+		} else if (sp->debug & L1_DEB_INTSTAT) {
+			sprintf(tmp, "avm IntStatus %x", sval);
+			debugl1(sp, tmp);
+		}
+		if (!(sval & AVM_A1_STAT_HSCX)) {
+			val = readreg(sp->hscx[1], HSCX_ISTA);
+			if (val) {
+				hscx_int_main(sp, val);
+				stat |= 1;
+			}
+		}
+		if (!(sval & AVM_A1_STAT_ISAC)) {
+			val = readreg(sp->isac, ISAC_ISTA);
+			if (val) {
+				isac_interrupt(sp, val);
+				stat |= 2;
+			}
+		}
+	}
+	if (stat & 1) {
+		writereg(sp->hscx[0], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[1], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[0], HSCX_MASK, 0x0);
+		writereg(sp->hscx[1], HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writereg(sp->isac, ISAC_MASK, 0xFF);
+		writereg(sp->isac, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->isac;
+
+	/* 16.3 IOM 2 Mode */
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_ADF2, 0x80);
+	writereg(adr, ISAC_SQXR, 0x2f);
+	writereg(adr, ISAC_SPCR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x2);
+	writereg(adr, ISAC_STCR, 0x70);
+	writereg(adr, ISAC_MODE, 0xc9);
+	writereg(adr, ISAC_TIMR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x0);
+	writereg(adr, ISAC_CMDR, 0x41);
+	writereg(adr, ISAC_CIX0, (1 << 2) | 3);
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writereg(sp->hscx[hscx], HSCX_CCR1, 0x85);
+	writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XBCH, 0x0);
+	writereg(sp->hscx[hscx], HSCX_RLCR, 0x0);
+
+	switch (mode) {
+	case (0):
+		writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+		writereg(sp->hscx[hscx], HSCX_TSAX, 0xff);
+		writereg(sp->hscx[hscx], HSCX_TSAR, 0xff);
+		writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+		writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		writereg(sp->hscx[hscx], HSCX_MODE, 0x84);
+		break;
+	case (1):
+		if (ichan == 0) {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		} else {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		}
+		writereg(sp->hscx[hscx], HSCX_MODE, 0xe4);
+		writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+		break;
+	case (2):
+		if (ichan == 0) {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		} else {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		}
+		writereg(sp->hscx[hscx], HSCX_MODE, 0x8c);
+		writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+		break;
+	}
+	writereg(sp->hscx[hscx], HSCX_ISTA, 0x00);
+}
+
+inline static void
+release_ioregs(struct IsdnCard *card, int mask)
+{
+	release_region(card->sp->cfg_reg, 8);
+	if (mask & 1)
+		release_region(card->sp->isac, 32);
+	if (mask & 2)
+		release_region(card->sp->isac - 0x400, 1);
+	if (mask & 4)
+		release_region(card->sp->hscx[0], 32);
+	if (mask & 8)
+		release_region(card->sp->hscx[0] - 0x400, 1);
+	if (mask & 0x10)
+		release_region(card->sp->hscx[1], 32);
+	if (mask & 0x20)
+		release_region(card->sp->hscx[1] - 0x400, 1);
+}
+
+void
+release_io_avm_a1(struct IsdnCard *card)
+{
+	release_ioregs(card, 0x3f);
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->hscx[1], HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = readreg(sp->hscx[0], HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = readreg(sp->hscx[0], HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[1], HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[0], HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->isac, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = readreg(sp->isac, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	writereg(sp->isac, ISAC_MASK, 0);
+	writereg(sp->isac, ISAC_CMDR, 0x41);
+}
+
+int
+initavm_a1(struct IsdnCardState *sp)
+{
+	int ret;
+	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);
+		sprintf(tmp, "IRQ %d count %d", sp->irq,
+			kstat.interrupts[sp->irq]);
+		debugl1(sp, tmp);
+		if (kstat.interrupts[sp->irq] == sp->counter) {
+			printk(KERN_WARNING
+			       "AVM A1: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_avm_a1(struct IsdnCard *card)
+{
+	byte val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_NOTICE "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
+	if (sp->typ != ISDN_CTYPE_A1)
+		return (0);
+
+	sp->cfg_reg = card->para[1] + 0x1800;
+	sp->isac = card->para[1] + 0x1400;
+	sp->hscx[0] = card->para[1] + 0x400;
+	sp->hscx[1] = card->para[1] + 0xc00;
+	sp->irq = card->para[0];
+	if (check_region((sp->cfg_reg), 8)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       sp->cfg_reg,
+		       sp->cfg_reg + 8);
+		return (0);
+	} else {
+		request_region(sp->cfg_reg, 8, "avm cfg");
+	}
+	if (check_region((sp->isac), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac ports %x-%x already in use\n",
+		       CardType[sp->typ],
+		       sp->isac,
+		       sp->isac + 32);
+		release_ioregs(card, 0);
+		return (0);
+	} else {
+		request_region(sp->isac, 32, "HiSax isac");
+	}
+	if (check_region((sp->isac - 0x400), 1)) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac fifo port %x already in use\n",
+		       CardType[sp->typ],
+		       sp->isac - 0x400);
+		release_ioregs(card, 1);
+		return (0);
+	} else {
+		request_region(sp->isac - 0x400, 1, "HiSax isac fifo");
+	}
+	if (check_region((sp->hscx[0]), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A ports %x-%x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[0],
+		       sp->hscx[0] + 32);
+		release_ioregs(card, 3);
+		return (0);
+	} else {
+		request_region(sp->hscx[0], 32, "HiSax hscx A");
+	}
+	if (check_region((sp->hscx[0] - 0x400), 1)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A fifo port %x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[0] - 0x400);
+		release_ioregs(card, 7);
+		return (0);
+	} else {
+		request_region(sp->hscx[0] - 0x400, 1, "HiSax hscx A fifo");
+	}
+	if (check_region((sp->hscx[1]), 32)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B ports %x-%x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[1],
+		       sp->hscx[1] + 32);
+		release_ioregs(card, 0xf);
+		return (0);
+	} else {
+		request_region(sp->hscx[1], 32, "HiSax hscx B");
+	}
+	if (check_region((sp->hscx[1] - 0x400), 1)) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B fifo port %x already in use\n",
+		       CardType[sp->typ],
+		       sp->hscx[1] - 0x400);
+		release_ioregs(card, 0x1f);
+		return (0);
+	} else {
+		request_region(sp->hscx[1] - 0x400, 1, "HiSax hscx B fifo");
+	}
+	save_flags(flags);
+	byteout(sp->cfg_reg, 0x0);
+	sti();
+	HZDELAY(HZ / 5 + 1);
+	byteout(sp->cfg_reg, 0x1);
+	HZDELAY(HZ / 5 + 1);
+	byteout(sp->cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	val = sp->irq;
+	if (val == 9)
+		val = 2;
+	byteout(sp->cfg_reg + 1, val);
+	HZDELAY(HZ / 5 + 1);
+	byteout(sp->cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	restore_flags(flags);
+
+	val = bytein(sp->cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg, val);
+	val = bytein(sp->cfg_reg + 3);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg + 3, val);
+	val = bytein(sp->cfg_reg + 2);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg + 2, val);
+	byteout(sp->cfg_reg, 0x14);
+	byteout(sp->cfg_reg, 0x18);
+	val = bytein(sp->cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       sp->cfg_reg, val);
+
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d cfg:%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->cfg_reg);
+	printk(KERN_NOTICE
+	       "HiSax: isac:%x/%x\n",
+	       sp->isac, sp->isac - 0x400);
+	printk(KERN_NOTICE
+	       "HiSax: hscx A:%x/%x  hscx B:%x/%x\n",
+	       sp->hscx[0], sp->hscx[0] - 0x400,
+	       sp->hscx[1], sp->hscx[1] - 0x400);
+	verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf;
+	verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "AVM A1: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = readreg(sp->isac, ISAC_RBCH);
+	printk(KERN_INFO "AVM A1: ISAC %s\n",
+	       ISACVersion(val));
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		       "AVM A1: wrong HSCX versions check IO address\n");
+		release_io_avm_a1(card);
+		return (0);
+	}
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/avm_a1.h linux/drivers/isdn/hisax/avm_a1.h
--- v2.1.26/linux/drivers/isdn/hisax/avm_a1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/avm_a1.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,25 @@
+/* $Id: avm_a1.h,v 1.2 1997/01/21 22:14:36 keil Exp $
+ *
+ * avm_a1.h   Header for AVM A1 (Fritz) ISDN card
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: avm_a1.h,v $
+ * Revision 1.2  1997/01/21 22:14:36  keil
+ * cleanups
+ *
+ * Revision 1.1  1996/10/12 21:42:40  keil
+ * Initial revision
+ *
+ *
+*/
+
+#define	 AVM_A1_STAT_ISAC	0x01
+#define	 AVM_A1_STAT_HSCX	0x02
+#define	 AVM_A1_STAT_TIMER	0x04
+
+extern	void avm_a1_report(struct IsdnCardState *sp);
+extern  void release_io_avm_a1(struct IsdnCard *card);
+extern	int  setup_avm_a1(struct IsdnCard *card);
+extern  int  initavm_a1(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/buffers.c linux/drivers/isdn/hisax/buffers.c
--- v2.1.26/linux/drivers/isdn/hisax/buffers.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/buffers.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,326 @@
+/* $Id: buffers.c,v 1.1 1996/10/13 20:04:49 keil Exp $
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: buffers.c,v $
+ * Revision 1.1  1996/10/13 20:04:49  keil
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#undef SMALLOC_DEBUG
+
+void
+BufPoolInit(struct BufPool *bp, int order, int bpps,
+	    int maxpages)
+{
+#ifdef DEBUG_MAGIC
+	generateerror
+	    bp->magic = 010167;
+#endif
+
+#if 0
+	printk(KERN_DEBUG "BufPoolInit bp %x\n", bp);
+#endif
+
+	bp->freelist = NULL;
+	bp->pageslist = NULL;
+	bp->pageorder = order;
+	bp->pagescount = 0;
+	bp->bpps = bpps;
+	bp->bufsize = BUFFER_SIZE(order, bpps);
+	bp->maxpages = maxpages;
+}
+
+int
+BufPoolAdd(struct BufPool *bp, int priority)
+{
+	struct Pages   *ptr;
+	byte           *bptr;
+	int             i;
+	struct BufHeader *bh = NULL, *prev, *first;
+
+#if 0
+	printk(KERN_DEBUG "BufPoolAdd bp %x\n", bp);
+#endif
+
+	ptr = (struct Pages *) __get_free_pages(priority, bp->pageorder, 0);
+	if (!ptr) {
+		printk(KERN_WARNING "BufPoolAdd couldn't get pages!\n");
+		return (-1);
+	}
+#if 0
+	printk(KERN_DEBUG "Order %d pages allocated at %x\n", bp->pageorder, ptr);
+#endif
+
+	ptr->next = bp->pageslist;
+	bp->pageslist = ptr;
+	bp->pagescount++;
+
+	bptr = (byte *) ptr + sizeof(struct Pages *);
+
+	i = bp->bpps;
+	first = (struct BufHeader *) bptr;
+	prev = NULL;
+	while (i--) {
+		bh = (struct BufHeader *) bptr;
+#ifdef DEBUG_MAGIC
+		bh->magic = 020167;
+#endif
+		bh->next = prev;
+		prev = bh;
+		bh->bp = bp;
+		bptr += PART_SIZE(bp->pageorder, bp->bpps);
+	}
+
+	first->next = bp->freelist;
+	bp->freelist = bh;
+	return (0);
+}
+
+void
+BufPoolFree(struct BufPool *bp)
+{
+	struct Pages   *p;
+
+#if 0
+	printk(KERN_DEBUG "BufPoolFree bp %x\n", bp);
+#endif
+
+	while (bp->pagescount--) {
+		p = bp->pageslist->next;
+		free_pages((unsigned long) bp->pageslist, bp->pageorder);
+#if 0
+		printk(KERN_DEBUG "Free pages %x order %d\n", bp->pageslist, bp->pageorder);
+#endif
+		bp->pageslist = p;
+	}
+}
+
+int
+BufPoolGet(struct BufHeader **bh,
+	   struct BufPool *bp, int priority, void *heldby, int where)
+{
+	long            flags;
+	int             i;
+
+#ifdef DEBUG_MAGIC
+	if (bp->magic != 010167) {
+		printk(KERN_DEBUG "BufPoolGet: not a BufHeader\n");
+		return (-1);
+	}
+#endif
+
+	save_flags(flags);
+	cli();
+	i = 0;
+	while (!0) {
+		if (bp->freelist) {
+			*bh = bp->freelist;
+			bp->freelist = bp->freelist->next;
+			(*bh)->heldby = heldby;
+			(*bh)->where = where;
+			restore_flags(flags);
+			return (0);
+		}
+		if ((i == 0) && (bp->pagescount < bp->maxpages)) {
+                        if (BufPoolAdd(bp, priority)) {
+                                restore_flags(flags);
+                                return -1;
+                        }
+			i++;
+		} else {
+			*bh = NULL;
+			restore_flags(flags);
+			return (-1);
+		}
+	}
+
+}
+
+void
+BufPoolRelease(struct BufHeader *bh)
+{
+	struct BufPool *bp;
+	long            flags;
+
+#ifdef DEBUG_MAGIC
+	if (bh->magic != 020167) {
+		printk(KERN_DEBUG "BufPoolRelease: not a BufHeader\n");
+		printk(KERN_DEBUG "called from %x\n", __builtin_return_address(0));
+		return;
+	}
+#endif
+
+	bp = bh->bp;
+
+#ifdef DEBUG_MAGIC
+	if (bp->magic != 010167) {
+		printk(KERN_DEBUG "BufPoolRelease: not a BufPool\n");
+		return;
+	}
+#endif
+
+	save_flags(flags);
+	cli();
+	bh->next = bp->freelist;
+	bp->freelist = bh;
+	restore_flags(flags);
+}
+
+void
+BufQueueLink(struct BufQueue *bq,
+	     struct BufHeader *bh)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	if (!bq->head)
+		bq->head = bh;
+	if (bq->tail)
+		bq->tail->next = bh;
+	bq->tail = bh;
+	bh->next = NULL;
+	restore_flags(flags);
+}
+
+void
+BufQueueLinkFront(struct BufQueue *bq,
+		  struct BufHeader *bh)
+{
+	unsigned long   flags;
+
+	save_flags(flags);
+	cli();
+	bh->next = bq->head;
+	bq->head = bh;
+	if (!bq->tail)
+		bq->tail = bh;
+	restore_flags(flags);
+}
+
+int
+BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq)
+{
+	long            flags;
+
+	save_flags(flags);
+	cli();
+
+	if (bq->head) {
+		if (bq->tail == bq->head)
+			bq->tail = NULL;
+		*bh = bq->head;
+		bq->head = (*bh)->next;
+		restore_flags(flags);
+		return (0);
+	} else {
+		restore_flags(flags);
+		return (-1);
+	}
+}
+
+void
+BufQueueInit(struct BufQueue *bq)
+{
+#ifdef DEBUG_MAGIC
+	bq->magic = 030167;
+#endif
+	bq->head = NULL;
+	bq->tail = NULL;
+}
+
+void
+BufQueueRelease(struct BufQueue *bq)
+{
+	struct BufHeader *bh;
+
+	while (bq->head) {
+		BufQueueUnlink(&bh, bq);
+		BufPoolRelease(bh);
+	}
+}
+
+int
+BufQueueLength(struct BufQueue *bq)
+{
+	int             i = 0;
+	struct BufHeader *bh;
+
+	bh = bq->head;
+	while (bh) {
+		i++;
+		bh = bh->next;
+	}
+	return (i);
+}
+
+void
+BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+		int releasetoo)
+{
+	long            flags;
+	struct BufHeader *sp;
+
+	save_flags(flags);
+	cli();
+
+	while (!0) {
+		sp = q->head;
+		if (!sp)
+			break;
+		if ((sp->primitive == pr) && (sp->heldby == heldby)) {
+			q->head = sp->next;
+			if (q->tail == sp)
+				q->tail = NULL;
+			if (releasetoo)
+				BufPoolRelease(sp);
+		} else
+			break;
+	}
+
+	sp = q->head;
+	if (sp)
+		while (sp->next) {
+			if ((sp->next->primitive == pr) && (sp->next->heldby == heldby)) {
+				if (q->tail == sp->next)
+					q->tail = sp;
+				if (releasetoo)
+					BufPoolRelease(sp->next);
+				sp->next = sp->next->next;
+			} else
+				sp = sp->next;
+		}
+	restore_flags(flags);
+}
+
+void
+Sfree(byte * ptr)
+{
+#ifdef SMALLOC_DEBUG
+	printk(KERN_DEBUG "Sfree %x\n", (unsigned int)ptr);
+#endif
+	kfree(ptr);
+}
+
+byte           *
+Smalloc(int size, int pr, char *why)
+{
+	byte           *p;
+
+	p = (byte *) kmalloc(size, pr);
+#ifdef SMALLOC_DEBUG
+	printk(KERN_DEBUG "Smalloc %s size %d res %x\n", why, size, (unsigned int)p);
+#endif
+	return (p);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/callc.c linux/drivers/isdn/hisax/callc.c
--- v2.1.26/linux/drivers/isdn/hisax/callc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/callc.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,1946 @@
+/* $Id: callc.c,v 1.20 1997/02/17 00:32:47 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: callc.c,v $
+ * Revision 1.20  1997/02/17 00:32:47  keil
+ * Bugfix: No Busy reported to LL
+ *
+ * Revision 1.19  1997/02/14 12:23:10  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.18  1997/02/11 01:36:58  keil
+ * Changed setup-interface (incoming and outgoing), cause reporting
+ *
+ * Revision 1.17  1997/02/09 00:23:10  keil
+ * new interface handling, one interface per card
+ * some changes in debug and leased line mode
+ *
+ * Revision 1.16  1997/01/27 23:17:03  keil
+ * delete timers while unloading
+ *
+ * Revision 1.15  1997/01/27 16:00:38  keil
+ * D-channel shutdown delay; improved callback
+ *
+ * Revision 1.14  1997/01/21 22:16:39  keil
+ * new statemachine; leased line support; cleanup for 2.0
+ *
+ * Revision 1.13  1996/12/08 19:51:17  keil
+ * bugfixes from Pekka Sarnila
+ *
+ * Revision 1.12  1996/11/26 20:20:03  keil
+ * fixed warning while compile
+ *
+ * Revision 1.11  1996/11/26 18:43:17  keil
+ * change ioctl 555 --> 55 (555 didn't work)
+ *
+ * Revision 1.10  1996/11/26 18:06:07  keil
+ * fixed missing break statement,ioctl 555 reset modcount
+ *
+ * Revision 1.9  1996/11/18 20:23:19  keil
+ * log writebuf channel not open changed
+ *
+ * Revision 1.8  1996/11/06 17:43:17  keil
+ * more changes for 2.1.X;block fixed ST_PRO_W
+ *
+ * Revision 1.7  1996/11/06 15:13:51  keil
+ * typo 0x64 --->64 in debug code
+ *
+ * Revision 1.6  1996/11/05 19:40:33  keil
+ * X.75 windowsize
+ *
+ * Revision 1.5  1996/10/30 10:11:06  keil
+ * debugging LOCK changed;ST_REL_W EV_HANGUP added
+ *
+ * Revision 1.4  1996/10/27 22:20:16  keil
+ * alerting bugfixes
+ * no static b-channel<->channel mapping
+ *
+ * Revision 1.2  1996/10/16 21:29:45  keil
+ * compile bug as "not module"
+ * Callback with euro
+ *
+ * Revision 1.1  1996/10/13 20:04:50  keil
+ * Initial revision
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < 0x020111)
+extern long mod_use_count_;
+#define MOD_USE_COUNT mod_use_count_
+#else
+#define MOD_USE_COUNT ((&__this_module)->usecount)
+#endif
+#endif				/* MODULE */
+
+const char *l4_revision = "$Revision: 1.20 $";
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+extern void HiSax_mod_dec_use_count(void);
+extern void HiSax_mod_inc_use_count(void);
+
+static int init_ds(struct Channel *chanp, int incoming);
+static void release_ds(struct Channel *chanp);
+
+static struct Fsm callcfsm =
+{NULL, 0, 0};
+static struct Fsm lcfsm =
+{NULL, 0, 0};
+
+static int chancount = 0;
+
+/* Flags for remembering action done in l4 */
+
+#define  FLG_START_D	0x0001
+#define  FLG_ESTAB_D	0x0002
+#define  FLG_CALL_SEND	0x0004
+#define  FLG_CALL_REC   0x0008
+#define  FLG_CALL_ALERT	0x0010
+#define  FLG_START_B	0x0020
+#define  FLG_CONNECT_B	0x0040
+#define  FLG_LL_DCONN	0x0080
+#define  FLG_LL_BCONN	0x0100
+#define  FLG_DISC_SEND	0x0200
+#define  FLG_DISC_REC	0x0400
+#define  FLG_REL_REC	0x0800
+
+#define  SETBIT(flg, item)  flg |= item
+#define  RESBIT(flg, item)  flg &= (~item)
+
+/*
+ * Because of callback it's a good idea to delay the shutdown of the d-channel
+ */
+#define	DREL_TIMER_VALUE 30000
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*
+hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp)
+			if (cards[i].sp->myid == driverid)
+				return (cards[i].sp);
+	return (struct IsdnCardState *) 0;
+}
+
+static void
+link_debug(struct Channel *chanp, char *s, int direction)
+{
+	char tmp[100], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(tmp, "%s Channel %d %s %s\n", tm, chanp->chan,
+		direction ? "LL->HL" : "HL->LL", s);
+	HiSax_putstatus(chanp->sp, tmp);
+}
+
+
+enum {
+	ST_NULL,		/*  0 inactive */
+	ST_OUT_WAIT_D,		/*  1 outgoing, awaiting d-channel establishment */
+	ST_IN_WAIT_D,		/*  2 incoming, awaiting d-channel establishment */
+	ST_OUT_DIAL,		/*  3 outgoing, SETUP send; awaiting confirm */
+	ST_IN_WAIT_LL,		/*  4 incoming call received; wait for LL confirm */
+	ST_IN_ALERT_SEND,	/*  5 incoming call received; ALERT send */
+	ST_IN_WAIT_CONN_ACK,	/*  6 incoming CONNECT send; awaiting CONN_ACK */
+	ST_WAIT_BCONN,		/*  7 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
+	ST_ACTIVE,		/*  8 active, b channel prot. established */
+	ST_WAIT_BRELEASE,	/*  9 call clear. (initiator), awaiting b channel prot. rel. */
+	ST_WAIT_BREL_DISC,	/* 10 call clear. (receiver), DISCONNECT req. received */
+	ST_WAIT_DCOMMAND,	/* 11 call clear. (receiver), awaiting DCHANNEL message */
+	ST_WAIT_DRELEASE,	/* 12 DISCONNECT sent, awaiting RELEASE */
+	ST_WAIT_D_REL_CNF,	/* 13 RELEASE sent, awaiting RELEASE confirm */
+	ST_WAIT_DSHUTDOWN,	/*  14 awaiting d-channel shutdown */
+};
+
+#define STATE_COUNT (ST_WAIT_DSHUTDOWN +1)
+
+static char *strState[] =
+{
+	"ST_NULL",
+	"ST_OUT_WAIT_D",
+	"ST_IN_WAIT_D",
+	"ST_OUT_DIAL",
+	"ST_IN_WAIT_LL",
+	"ST_IN_ALERT_SEND",
+	"ST_IN_WAIT_CONN_ACK",
+	"ST_WAIT_BCONN",
+	"ST_ACTIVE",
+	"ST_WAIT_BRELEASE",
+	"ST_WAIT_BREL_DISC",
+	"ST_WAIT_DCOMMAND",
+	"ST_WAIT_DRELEASE",
+	"ST_WAIT_D_REL_CNF",
+	"ST_WAIT_DSHUTDOWN",
+};
+
+enum {
+	EV_DIAL,		/*  0 */
+	EV_SETUP_CNF,		/*  1 */
+	EV_ACCEPTB,		/*  2 */
+	EV_DISCONNECT_IND,	/*  3 */
+	EV_RELEASE_CNF,		/*  4 */
+	EV_DLEST,		/*  5 */
+	EV_DLRL,		/*  6 */
+	EV_SETUP_IND,		/*  7 */
+	EV_RELEASE_IND,		/*  8 */
+	EV_ACCEPTD,		/*  9 */
+	EV_SETUP_CMPL_IND,	/* 10 */
+	EV_BC_EST,		/* 11 */
+	EV_WRITEBUF,		/* 12 */
+	EV_DATAIN,		/* 13 */
+	EV_HANGUP,		/* 14 */
+	EV_BC_REL,		/* 15 */
+	EV_CINF,		/* 16 */
+	EV_SUSPEND,		/* 17 */
+	EV_RESUME,		/* 18 */
+	EV_SHUTDOWN_D,		/* 19 */
+	EV_NOSETUP_RSP,		/* 20 */
+	EV_SETUP_ERR,		/* 21 */
+	EV_CONNECT_ERR,		/* 22 */
+	EV_RELEASE_ERR,		/* 23 */
+};
+
+#define EVENT_COUNT (EV_RELEASE_ERR +1)
+
+static char *strEvent[] =
+{
+	"EV_DIAL",
+	"EV_SETUP_CNF",
+	"EV_ACCEPTB",
+	"EV_DISCONNECT_IND",
+	"EV_RELEASE_CNF",
+	"EV_DLEST",
+	"EV_DLRL",
+	"EV_SETUP_IND",
+	"EV_RELEASE_IND",
+	"EV_ACCEPTD",
+	"EV_SETUP_CMPL_IND",
+	"EV_BC_EST",
+	"EV_WRITEBUF",
+	"EV_DATAIN",
+	"EV_HANGUP",
+	"EV_BC_REL",
+	"EV_CINF",
+	"EV_SUSPEND",
+	"EV_RESUME",
+	"EV_SHUTDOWN_D",
+	"EV_NOSETUP_RSP",
+	"EV_SETUP_ERR",
+	"EV_CONNECT_ERR",
+	"EV_RELEASE_ERR",
+};
+
+enum {
+	ST_LC_NULL,
+	ST_LC_ACTIVATE_WAIT,
+	ST_LC_DELAY,
+	ST_LC_ESTABLISH_WAIT,
+	ST_LC_CONNECTED,
+	ST_LC_FLUSH_WAIT,
+	ST_LC_FLUSH_DELAY,
+	ST_LC_RELEASE_WAIT,
+};
+
+#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
+
+static char *strLcState[] =
+{
+	"ST_LC_NULL",
+	"ST_LC_ACTIVATE_WAIT",
+	"ST_LC_DELAY",
+	"ST_LC_ESTABLISH_WAIT",
+	"ST_LC_CONNECTED",
+	"ST_LC_FLUSH_WAIT",
+	"ST_LC_FLUSH_DELAY",
+	"ST_LC_RELEASE_WAIT",
+};
+
+enum {
+	EV_LC_ESTABLISH,
+	EV_LC_PH_ACTIVATE,
+	EV_LC_PH_DEACTIVATE,
+	EV_LC_DL_ESTABLISH,
+	EV_LC_TIMER,
+	EV_LC_DL_FLUSH,
+	EV_LC_DL_RELEASE,
+	EV_LC_FLUSH,
+	EV_LC_RELEASE,
+};
+
+#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
+
+static char *strLcEvent[] =
+{
+	"EV_LC_ESTABLISH",
+	"EV_LC_PH_ACTIVATE",
+	"EV_LC_PH_DEACTIVATE",
+	"EV_LC_DL_ESTABLISH",
+	"EV_LC_TIMER",
+	"EV_LC_DL_FLUSH",
+	"EV_LC_DL_RELEASE",
+	"EV_LC_FLUSH",
+	"EV_LC_RELEASE",
+};
+
+#define LC_D  0
+#define LC_B  1
+
+static inline void
+l4_deliver_cause(struct Channel *chanp)
+{
+	isdn_ctrl ic;
+	
+	if (chanp->para.cause<0)
+		return;
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	if (chanp->sp->protocol == ISDN_PTYPE_EURO)
+		sprintf(ic.parm.num, "E%02X%02X", chanp->para.loc,
+			chanp->para.cause);
+	else
+		sprintf(ic.parm.num, "%02X%02X", chanp->para.loc,
+			chanp->para.cause);
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/*
+ * Dial out
+ */
+static void
+l4_prep_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	isdn_ctrl *ic = arg;
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_OUT_WAIT_D);
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+	chanp->para.setup = ic->parm.setup;
+	if (!strcmp(chanp->para.setup.eazmsn, "0"))
+		chanp->para.setup.eazmsn[0] = '\0';
+
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->lc_b.l2_start = !0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			chanp->lc_b.l2_establish = !0;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			chanp->lc_b.l2_establish = 0;
+			break;
+		default:
+			printk(KERN_WARNING "l4_prep_dialout unknown protocol\n");
+			break;
+	}
+	if (chanp->Flags & FLG_ESTAB_D) {
+		FsmEvent(fi, EV_DLEST, NULL);
+	} else {
+		chanp->Flags = FLG_START_D;
+		if (chanp->leased) {
+			chanp->lc_d.l2_establish = 0;
+		}
+		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+l4_do_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_OUT_DIAL);
+	if (chanp->leased) {
+		chanp->para.bchannel = (chanp->chan & 1) + 1;
+		FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+	} else {
+		SETBIT(chanp->Flags, FLG_ESTAB_D);
+		chanp->para.callref = chanp->outcallref;
+		chanp->outcallref++;
+		if (chanp->outcallref == 128)
+			chanp->outcallref = 64;
+		chanp->is.l4.l4l3(&chanp->is, CC_SETUP_REQ, NULL);
+		SETBIT(chanp->Flags, FLG_CALL_SEND);
+	}
+}
+
+static void
+l4_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DCONN", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	init_ds(chanp, 0);
+	SETBIT(chanp->Flags, FLG_START_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+static void
+l4_go_active(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_ACTIVE);
+	chanp->data_open = !0;
+	SETBIT(chanp->Flags, FLG_CONNECT_B);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_BCONN", 0);
+	SETBIT(chanp->Flags, FLG_LL_BCONN);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_BCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/* incomming call */
+
+static void
+l4_start_dchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_D);
+	FsmDelTimer(&chanp->drel_timer, 61);
+	if (chanp->Flags & FLG_ESTAB_D) {
+		FsmEvent(fi, EV_DLEST, NULL);
+	} else {
+		chanp->Flags = FLG_START_D;
+		FsmEvent(&chanp->lc_d.lcfi, EV_LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+l4_deliver_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
+	char txt[32];
+
+	/*
+	 * Report incoming calls only once to linklevel, use CallFlags
+	 * which is set to 3 with each broadcast message in isdnl1.c
+	 * and resetted if a interface  answered the STAT_ICALL.
+	 */
+	if ((chanp->sp) && (chanp->sp->CallFlags == 3)) {
+		FsmChangeState(fi, ST_IN_WAIT_LL);
+		SETBIT(chanp->Flags, FLG_ESTAB_D);
+		SETBIT(chanp->Flags, FLG_CALL_REC);
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_ICALL", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_ICALL;
+		ic.arg = chanp->chan;
+		/*
+		 * No need to return "unknown" for calls without OAD,
+		 * cause that's handled in linklevel now (replaced by '0')
+		 */
+		ic.parm.setup = chanp->para.setup;
+		ret = chanp->sp->iif.statcallb(&ic);
+		if (chanp->debug & 1) {
+			sprintf(txt, "statcallb ret=%d", ret);
+			link_debug(chanp, txt, 1);
+		}
+		if (ret)	/* if a interface knows this call, reset the CallFlag
+				   * to avoid a second Call report to the linklevel
+				 */
+			chanp->sp->CallFlags &= ~(chanp->chan + 1);
+		switch (ret) {
+			case 1:	/* OK, anybody likes this call */
+				FsmChangeState(fi, ST_IN_ALERT_SEND);
+				SETBIT(chanp->Flags, FLG_CALL_ALERT);
+				chanp->is.l4.l4l3(&chanp->is, CC_ALERTING_REQ, NULL);
+				break;
+			case 2:	/* Rejecting Call */
+				RESBIT(chanp->Flags, FLG_CALL_REC);
+				break;
+			case 0:	/* OK, nobody likes this call */
+			default:	/* statcallb problems */
+				chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+				FsmChangeState(fi, ST_NULL);
+				chanp->Flags = FLG_ESTAB_D;
+				FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
+				break;
+		}
+	} else {
+		chanp->is.l4.l4l3(&chanp->is, CC_IGNORE, NULL);
+		FsmChangeState(fi, ST_NULL);
+		chanp->Flags = FLG_ESTAB_D;
+		FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
+	}
+}
+
+static void
+l4_send_dconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+	chanp->is.l4.l4l3(&chanp->is, CC_SETUP_RSP, NULL);
+}
+
+static void
+l4_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	SETBIT(chanp->Flags, FLG_LL_DCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DCONN", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DCONN;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = !0;
+	chanp->lc_b.l2_start = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			chanp->lc_b.l2_establish = !0;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			chanp->lc_b.l2_establish = 0;
+			break;
+		default:
+			printk(KERN_WARNING "r9 unknown protocol\n");
+			break;
+	}
+	init_ds(chanp, !0);
+	SETBIT(chanp->Flags, FLG_START_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_ESTABLISH, NULL);
+}
+
+/* Call clearing */
+
+static void
+l4_reject_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	chanp->para.cause = 0x15;	/* Call Rejected */
+	chanp->is.l4.l4l3(&chanp->is, CC_REJECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_cancel_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	chanp->para.cause = 0x10;	/* Normal Call Clearing */
+	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_shutdown_d(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	FsmDelTimer(&chanp->drel_timer, 62);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_timeout_d(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmChangeState(fi, ST_NULL);
+	chanp->Flags = FLG_ESTAB_D;
+	FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+}
+
+static void
+l4_go_null(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+}
+
+static void
+l4_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_BRELEASE);
+	RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_send_d_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+
+	if (chanp->Flags & (FLG_DISC_REC | FLG_REL_REC))
+		return;
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	chanp->para.cause = 0x10;	/* Normal Call Clearing */
+	chanp->is.l4.l4l3(&chanp->is, CC_DISCONNECT_REQ, NULL);
+	SETBIT(chanp->Flags, FLG_DISC_SEND);
+}
+
+static void
+l4_released_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DCOMMAND);
+	chanp->data_open = 0;
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	release_ds(chanp);
+	RESBIT(chanp->Flags, FLG_START_B);
+}
+
+
+static void
+l4_release_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	SETBIT(chanp->Flags, FLG_DISC_REC);
+	FsmChangeState(fi, ST_WAIT_BREL_DISC);
+	RESBIT(chanp->Flags, FLG_CONNECT_B);
+	FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_received_d_rel(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	SETBIT(chanp->Flags, FLG_REL_REC);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		RESBIT(chanp->Flags, FLG_CALL_SEND);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_FLUSH, NULL);
+	RESBIT(chanp->Flags, FLG_ESTAB_D);
+	RESBIT(chanp->Flags, FLG_DISC_SEND);
+	RESBIT(chanp->Flags, FLG_CALL_REC);
+	RESBIT(chanp->Flags, FLG_CALL_ALERT);
+}
+
+static void
+l4_received_d_relcnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		RESBIT(chanp->Flags, FLG_CALL_SEND);
+		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);
+}
+
+static void
+l4_received_d_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_D_REL_CNF);
+	SETBIT(chanp->Flags, FLG_DISC_REC);
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & (FLG_LL_DCONN | FLG_CALL_SEND)) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		RESBIT(chanp->Flags, FLG_CALL_SEND);
+		RESBIT(chanp->Flags, FLG_CALL_ALERT);
+		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_CALL_ALERT);
+	chanp->is.l4.l4l3(&chanp->is, CC_RELEASE_REQ, NULL);
+}
+
+/* processing charge info */
+static void
+l4_charge_info(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_CINF;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "%d", chanp->para.chargeinfo);
+	chanp->sp->iif.statcallb(&ic);
+}
+
+/* error procedures */
+
+static void
+l4_no_dchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_NODCH", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_NODCH;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_no_dchan_ready(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DHUP", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static void
+l4_no_dchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+
+static void
+l4_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->Flags = 0;
+	FsmChangeState(fi, ST_NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+	if (chanp->debug & 1)
+		link_debug(chanp, "STAT_DHUP", 0);
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_DHUP;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static void
+l4_setup_err(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+}
+
+static void
+l4_connect_err(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	SETBIT(chanp->Flags, FLG_DISC_SEND);	/* DISCONN was sent from L3 */
+}
+
+static void
+l4_active_dlrl(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_NULL);
+	if (chanp->Flags & FLG_CONNECT_B) {
+		chanp->lc_b.l2_establish = 0;	/* direct reset in lc_b.lcfi */
+		FsmEvent(&chanp->lc_b.lcfi, EV_LC_RELEASE, NULL);
+		RESBIT(chanp->Flags, FLG_CONNECT_B);
+	}
+	if (chanp->Flags & FLG_LL_BCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_BHUP", 0);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_BHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+		RESBIT(chanp->Flags, FLG_LL_BCONN);
+	}
+	if (chanp->Flags & FLG_START_B) {
+		release_ds(chanp);
+		RESBIT(chanp->Flags, FLG_START_B);
+	}
+	if (chanp->Flags & FLG_LL_DCONN) {
+		if (chanp->debug & 1)
+			link_debug(chanp, "STAT_DHUP", 0);
+		RESBIT(chanp->Flags, FLG_LL_DCONN);
+		if (chanp->sp->protocol == ISDN_PTYPE_EURO) {
+			chanp->para.cause  = 0x2f;
+			chanp->para.loc    = 0;
+		} else {
+			chanp->para.cause  = 0x70;
+			chanp->para.loc    = 0;
+		}
+		l4_deliver_cause(chanp);
+		ic.driver = chanp->sp->myid;
+		ic.command = ISDN_STAT_DHUP;
+		ic.arg = chanp->chan;
+		chanp->sp->iif.statcallb(&ic);
+	}
+	chanp->Flags = 0;
+	chanp->is.l4.l4l3(&chanp->is, CC_DLRL, NULL);
+	FsmEvent(&chanp->lc_d.lcfi, EV_LC_RELEASE, NULL);
+}
+/* *INDENT-OFF* */
+static struct FsmNode fnlist[] =
+{
+	{ST_NULL,		EV_DIAL,		l4_prep_dialout},
+	{ST_NULL,		EV_SETUP_IND,		l4_start_dchan},
+	{ST_NULL,		EV_SHUTDOWN_D,		l4_shutdown_d},
+	{ST_OUT_WAIT_D,		EV_DLEST,		l4_do_dialout},
+	{ST_OUT_WAIT_D,		EV_DLRL,		l4_no_dchan},
+	{ST_OUT_WAIT_D,		EV_HANGUP,		l4_no_dchan},
+	{ST_IN_WAIT_D,		EV_DLEST,		l4_deliver_call},
+	{ST_IN_WAIT_D,		EV_DLRL,		l4_no_dchan_in},
+	{ST_IN_WAIT_D,		EV_HANGUP,		l4_no_dchan_in},
+	{ST_OUT_DIAL,		EV_SETUP_CNF,		l4_init_bchan_out},
+	{ST_OUT_DIAL,		EV_HANGUP,		l4_cancel_call},
+	{ST_OUT_DIAL,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_OUT_DIAL,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_OUT_DIAL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_OUT_DIAL,		EV_NOSETUP_RSP,		l4_no_setup_rsp},
+	{ST_OUT_DIAL,		EV_SETUP_ERR,		l4_setup_err},
+	{ST_IN_WAIT_LL,		EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_WAIT_LL,		EV_ACCEPTD,		l4_send_dconnect},
+	{ST_IN_WAIT_LL,		EV_HANGUP,		l4_reject_call},
+	{ST_IN_WAIT_LL,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_WAIT_LL,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_WAIT_LL,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_ALERT_SEND,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_ALERT_SEND,	EV_ACCEPTD,		l4_send_dconnect},
+	{ST_IN_ALERT_SEND,	EV_HANGUP,		l4_send_d_disc},
+	{ST_IN_ALERT_SEND,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_ALERT_SEND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_SETUP_CMPL_IND,	l4_init_bchan_in},
+	{ST_IN_WAIT_CONN_ACK,	EV_HANGUP,		l4_send_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_IN_WAIT_CONN_ACK,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_IN_WAIT_CONN_ACK,	EV_CONNECT_ERR,		l4_connect_err},
+	{ST_WAIT_BCONN,		EV_BC_EST,		l4_go_active},
+	{ST_WAIT_BCONN,		EV_BC_REL,		l4_send_d_disc},
+	{ST_WAIT_BCONN,		EV_HANGUP,		l4_send_d_disc},
+	{ST_WAIT_BCONN,		EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_BCONN,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_BCONN,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_ACTIVE,		EV_CINF,		l4_charge_info},
+	{ST_ACTIVE,		EV_BC_REL,		l4_released_bchan},
+	{ST_ACTIVE,		EV_HANGUP,		l4_disconn_bchan},
+	{ST_ACTIVE,		EV_DISCONNECT_IND,	l4_release_bchan},
+	{ST_ACTIVE,		EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_ACTIVE,		EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_ACTIVE,		EV_DLRL,		l4_active_dlrl},
+	{ST_WAIT_BRELEASE,	EV_BC_REL,		l4_send_d_disc},
+	{ST_WAIT_BRELEASE,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_BRELEASE,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_BREL_DISC,	EV_BC_REL,		l4_received_d_disc},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_BREL_DISC,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_DCOMMAND,	EV_HANGUP,		l4_send_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_DISCONNECT_IND,	l4_received_d_disc},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_CNF,		l4_received_d_relcnf},
+	{ST_WAIT_DCOMMAND,	EV_RELEASE_IND,		l4_received_d_rel},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_IND,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_CNF,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_RELEASE_ERR,		l4_timeout_d},
+	{ST_WAIT_DRELEASE,	EV_DIAL,		l4_no_dchan_ready},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_CNF,		l4_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_RELEASE_ERR,		l4_timeout_d},
+	{ST_WAIT_D_REL_CNF,	EV_DIAL,		l4_no_dchan_ready},
+	{ST_WAIT_DSHUTDOWN,	EV_DLRL,		l4_go_null},
+	{ST_WAIT_DSHUTDOWN,	EV_DIAL,		l4_prep_dialout},
+	{ST_WAIT_DSHUTDOWN,	EV_SETUP_IND,		l4_start_dchan},
+};
+/* *INDENT-ON* */
+
+
+
+
+
+
+
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+static void
+lc_r1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
+	FsmAddTimer(&lf->act_timer, 1000, EV_LC_TIMER, NULL, 50);
+	lf->st->ma.manl1(lf->st, PH_ACTIVATE, NULL);
+
+}
+
+static void
+lc_r6(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmDelTimer(&lf->act_timer, 50);
+	FsmChangeState(fi, ST_LC_DELAY);
+	FsmAddTimer(&lf->act_timer, 40, EV_LC_TIMER, NULL, 51);
+}
+
+static void
+lc_r2(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	if (lf->l2_establish) {
+		FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
+		if (lf->l2_start)
+			lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
+	} else {
+		FsmChangeState(fi, ST_LC_CONNECTED);
+		lf->lccall(lf, LC_ESTABLISH, NULL);
+	}
+}
+
+static void
+lc_r3(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_CONNECTED);
+	lf->lccall(lf, LC_ESTABLISH, NULL);
+}
+
+static void
+lc_r7(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_FLUSH_WAIT);
+	lf->st->ma.manl2(lf->st, DL_FLUSH, NULL);
+}
+
+static void
+lc_r4(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	if (lf->l2_establish) {
+		FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+		lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
+		/* This timer is for releasing the channel even
+		 * there is a hang in layer 2 ; 5 sec are a try
+		 */
+		FsmAddTimer(&lf->act_timer, 5000, EV_LC_TIMER, NULL, 53);
+	} else {
+		FsmChangeState(fi, ST_LC_NULL);
+		lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+		lf->lccall(lf, LC_RELEASE, NULL);
+	}
+}
+
+static void
+lc_r4_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_FLUSH_DELAY);
+	FsmAddTimer(&lf->act_timer, 50, EV_LC_TIMER, NULL, 52);
+}
+
+static void
+lc_r5_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmChangeState(fi, ST_LC_RELEASE_WAIT);
+	/* This delay is needed for send out the UA frame before
+	 * PH_DEACTIVATE the interface
+	 */
+	FsmAddTimer(&lf->act_timer, 10, EV_LC_TIMER, NULL, 54);
+}
+
+static void
+lc_r5(struct FsmInst *fi, int event, void *arg)
+{
+	struct LcFsm *lf = fi->userdata;
+
+	FsmDelTimer(&lf->act_timer, 54);
+	FsmChangeState(fi, ST_LC_NULL);
+	lf->st->ma.manl1(lf->st, PH_DEACTIVATE, NULL);
+	lf->lccall(lf, LC_RELEASE, NULL);
+}
+/* *INDENT-OFF* */
+static struct FsmNode LcFnList[] =
+{
+	{ST_LC_NULL,  		EV_LC_ESTABLISH,	lc_r1},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_PH_ACTIVATE,	lc_r6},
+	{ST_LC_DELAY,		EV_LC_TIMER,		lc_r2},
+	{ST_LC_DELAY,		EV_LC_DL_ESTABLISH,	lc_r3},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_ESTABLISH,	lc_r3},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_RELEASE,		lc_r5},
+	{ST_LC_CONNECTED,	EV_LC_FLUSH,		lc_r7},
+	{ST_LC_CONNECTED,	EV_LC_RELEASE,		lc_r4},
+	{ST_LC_CONNECTED,	EV_LC_DL_RELEASE,	lc_r5_1},
+	{ST_LC_FLUSH_WAIT,	EV_LC_DL_FLUSH,		lc_r4_1},
+	{ST_LC_FLUSH_DELAY,	EV_LC_TIMER,		lc_r4},
+	{ST_LC_RELEASE_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
+	{ST_LC_RELEASE_WAIT,	EV_LC_TIMER,		lc_r5},
+	{ST_LC_ACTIVATE_WAIT,	EV_LC_TIMER,		lc_r5},
+	{ST_LC_ESTABLISH_WAIT,	EV_LC_DL_RELEASE,	lc_r5},
+};
+/* *INDENT-ON* */
+
+
+
+
+
+
+
+#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
+
+void
+CallcNew(void)
+{
+	callcfsm.state_count = STATE_COUNT;
+	callcfsm.event_count = EVENT_COUNT;
+	callcfsm.strEvent = strEvent;
+	callcfsm.strState = strState;
+	FsmNew(&callcfsm, fnlist, FNCOUNT);
+
+	lcfsm.state_count = LC_STATE_COUNT;
+	lcfsm.event_count = LC_EVENT_COUNT;
+	lcfsm.strEvent = strLcEvent;
+	lcfsm.strState = strLcState;
+	FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
+}
+
+void
+CallcFree(void)
+{
+	FsmFree(&lcfsm);
+	FsmFree(&callcfsm);
+}
+
+static void
+release_ds(struct Channel *chanp)
+{
+	struct PStack *st = &chanp->ds;
+	struct IsdnCardState *sp;
+	struct HscxState *hsp;
+
+	sp = st->l1.hardware;
+	hsp = sp->hs + chanp->hscx;
+
+	close_hscxstate(hsp);
+
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			releasestack_isdnl2(st);
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_TRANS):
+			releasestack_transl2(st);
+			break;
+	}
+}
+
+static void
+cc_l1man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+static void
+cc_l2man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_RELEASE, NULL);
+			break;
+		case (DL_FLUSH):
+			FsmEvent(&chanp->lc_d.lcfi, EV_LC_DL_FLUSH, NULL);
+			break;
+	}
+}
+
+static void
+dcc_l1man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+static void
+dcc_l2man(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_ESTABLISH, NULL);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&chanp->lc_b.lcfi, EV_LC_DL_RELEASE, NULL);
+			break;
+	}
+}
+
+static void
+ll_handler(struct PStack *st, int pr,
+	   struct BufHeader *ibh)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	char tmp[64], tm[32];
+
+	switch (pr) {
+		case (CC_DISCONNECT_IND):
+			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+			break;
+		case (CC_RELEASE_CNF):
+			FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+			break;
+		case (CC_SETUP_IND):
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+			break;
+		case (CC_RELEASE_IND):
+			FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
+			break;
+		case (CC_SETUP_COMPLETE_IND):
+			FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+			break;
+		case (CC_SETUP_CNF):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_INFO_CHARGE):
+			FsmEvent(&chanp->fi, EV_CINF, NULL);
+			break;
+		case (CC_NOSETUP_RSP_ERR):
+			FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
+			break;
+		case (CC_SETUP_ERR):
+			FsmEvent(&chanp->fi, EV_SETUP_ERR, NULL);
+			break;
+		case (CC_CONNECT_ERR):
+			FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL);
+			break;
+		case (CC_RELEASE_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL);
+			break;
+		default:
+			jiftime(tm, jiffies);
+			sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n", tm, chanp->chan, pr);
+			HiSax_putstatus(chanp->sp, tmp);
+	}
+}
+
+static void
+init_is(struct Channel *chanp, unsigned int ces)
+{
+	struct PStack *st = &chanp->is;
+	struct IsdnCardState *sp = chanp->sp;
+	char tmp[128];
+
+	setstack_HiSax(st, sp);
+	st->l2.sap = 0;
+	st->l2.tei = 255;
+	st->l2.ces = ces;
+	st->l2.extended = !0;
+	st->l2.laptype = LAPD;
+	st->l2.window = 1;
+	st->l2.orig = !0;
+	st->l2.t200 = 1000;	/* 1000 milliseconds  */
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		st->l2.n200 = 3;	/* try 3 times        */
+		st->l2.t203 = 10000;	/* 10000 milliseconds */
+	} else {
+		st->l2.n200 = 4;	/* try 4 times        */
+		st->l2.t203 = 5000;	/* 5000 milliseconds  */
+	}
+	sprintf(tmp, "Channel %d q.921", chanp->chan);
+	setstack_isdnl2(st, tmp);
+	setstack_isdnl3(st, chanp);
+	st->l4.userdata = chanp;
+	st->l4.l2writewakeup = NULL;
+	st->l3.l3l4 = ll_handler;
+	st->l1.l1man = cc_l1man;
+	st->l2.l2man = cc_l2man;
+	st->pa = &chanp->para;
+	HiSax_addlist(sp, st);
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *s)
+{
+	char str[80], tm[32];
+	struct Channel *chanp = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
+	HiSax_putstatus(chanp->sp, str);
+}
+
+static void
+lc_debug(struct FsmInst *fi, char *s)
+{
+	char str[256], tm[32];
+	struct LcFsm *lf = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d lc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->sp, str);
+}
+
+static void
+dlc_debug(struct FsmInst *fi, char *s)
+{
+	char str[256], tm[32];
+	struct LcFsm *lf = fi->userdata;
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d dlc %s\n", tm, lf->ch->chan, s);
+	HiSax_putstatus(lf->ch->sp, str);
+}
+
+static void
+lccall_d(struct LcFsm *lf, int pr, void *arg)
+{
+	struct Channel *chanp = lf->ch;
+
+	switch (pr) {
+		case (LC_ESTABLISH):
+			FsmEvent(&chanp->fi, EV_DLEST, NULL);
+			break;
+		case (LC_RELEASE):
+			FsmEvent(&chanp->fi, EV_DLRL, NULL);
+			break;
+	}
+}
+
+static void
+lccall_b(struct LcFsm *lf, int pr, void *arg)
+{
+	struct Channel *chanp = lf->ch;
+
+	switch (pr) {
+		case (LC_ESTABLISH):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (LC_RELEASE):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
+	}
+}
+
+static void
+init_chan(int chan, struct IsdnCardState *csta, int hscx,
+	  unsigned int ces)
+{
+	struct Channel *chanp = csta->channel + chan;
+
+	chanp->sp = csta;
+	chanp->hscx = hscx;
+	chanp->chan = chan;
+	chanp->incoming = 0;
+	chanp->debug = 0;
+	chanp->Flags = 0;
+	chanp->leased = 0;
+	init_is(chanp, ces);
+
+	chanp->fi.fsm = &callcfsm;
+	chanp->fi.state = ST_NULL;
+	chanp->fi.debug = 0;
+	chanp->fi.userdata = chanp;
+	chanp->fi.printdebug = callc_debug;
+	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
+	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
+
+	chanp->lc_d.lcfi.fsm = &lcfsm;
+	chanp->lc_d.lcfi.state = ST_LC_NULL;
+	chanp->lc_d.lcfi.debug = 0;
+	chanp->lc_d.lcfi.userdata = &chanp->lc_d;
+	chanp->lc_d.lcfi.printdebug = lc_debug;
+	chanp->lc_d.type = LC_D;
+	chanp->lc_d.ch = chanp;
+	chanp->lc_d.st = &chanp->is;
+	chanp->lc_d.l2_establish = !0;
+	chanp->lc_d.l2_start = !0;
+	chanp->lc_d.lccall = lccall_d;
+	FsmInitTimer(&chanp->lc_d.lcfi, &chanp->lc_d.act_timer);
+
+	chanp->lc_b.lcfi.fsm = &lcfsm;
+	chanp->lc_b.lcfi.state = ST_LC_NULL;
+	chanp->lc_b.lcfi.debug = 0;
+	chanp->lc_b.lcfi.userdata = &chanp->lc_b;
+	chanp->lc_b.lcfi.printdebug = dlc_debug;
+	chanp->lc_b.type = LC_B;
+	chanp->lc_b.ch = chanp;
+	chanp->lc_b.st = &chanp->ds;
+	chanp->lc_b.l2_establish = !0;
+	chanp->lc_b.l2_start = !0;
+	chanp->lc_b.lccall = lccall_b;
+	FsmInitTimer(&chanp->lc_b.lcfi, &chanp->lc_b.act_timer);
+	chanp->outcallref = 64;
+	chanp->data_open = 0;
+}
+
+int
+CallcNewChan(struct IsdnCardState *csta)
+{
+	int ces;
+
+	chancount += 2;
+	ces = randomces();
+	init_chan(0, csta, 1, ces++);
+	ces %= 0xffff;
+	init_chan(1, csta, 0, ces++);
+	printk(KERN_INFO "HiSax: 2 channels added\n");
+	return (2);
+}
+
+static void
+release_is(struct Channel *chanp)
+{
+	struct PStack *st = &chanp->is;
+
+	releasestack_isdnl2(st);
+	releasestack_isdnl3(st);
+	HiSax_rmlist(st->l1.hardware, st);
+	BufQueueRelease(&st->l2.i_queue);
+}
+
+void
+CallcFreeChan(struct IsdnCardState *csta)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		FsmDelTimer(&csta->channel[i].drel_timer, 74);
+		FsmDelTimer(&csta->channel[i].dial_timer, 75);
+		FsmDelTimer(&csta->channel[i].lc_b.act_timer, 76);
+		FsmDelTimer(&csta->channel[i].lc_d.act_timer, 77);
+		if (csta->channel[i].Flags & FLG_START_B) {
+			release_ds(csta->channel + i);
+		}
+		release_is(csta->channel + i);
+	}
+}
+
+static void
+lldata_handler(struct PStack *st, int pr,
+	       void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	byte *ptr;
+	int size;
+	struct BufHeader *ibh = arg;
+
+	switch (pr) {
+		case (DL_DATA):
+			if (chanp->data_open) {
+				ptr = DATAPTR(ibh);
+				ptr += chanp->ds.l2.ihsize;
+				size = ibh->datasize - chanp->ds.l2.ihsize;
+				chanp->sp->iif.rcvcallb(chanp->sp->myid, chanp->chan, ptr, size);
+			}
+			BufPoolRelease(ibh);
+			break;
+		default:
+			printk(KERN_WARNING "lldata_handler unknown primitive %d\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr,
+		struct BufHeader *ibh)
+{
+	struct Channel *chanp = (struct Channel *) st->l4.userdata;
+	byte *ptr;
+
+	switch (pr) {
+		case (PH_DATA):
+			if (chanp->data_open) {
+				ptr = DATAPTR(ibh);
+				chanp->sp->iif.rcvcallb(chanp->sp->myid, chanp->chan, ptr, ibh->datasize);
+			}
+			BufPoolRelease(ibh);
+			break;
+		default:
+			printk(KERN_WARNING "lltrans_handler unknown primitive %d\n",
+			       pr);
+			break;
+	}
+}
+
+static void
+ll_writewakeup(struct PStack *st)
+{
+	struct Channel *chanp = st->l4.userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->sp->myid;
+	ic.command = ISDN_STAT_BSENT;
+	ic.arg = chanp->chan;
+	chanp->sp->iif.statcallb(&ic);
+}
+
+static int
+init_ds(struct Channel *chanp, int incoming)
+{
+	struct PStack *st = &chanp->ds;
+	struct IsdnCardState *sp = chanp->sp;
+	struct HscxState *hsp = sp->hs + chanp->hscx;
+	char tmp[128];
+
+	st->l1.hardware = sp;
+
+	hsp->mode = 2;
+	hsp->transbufsize = 4000;
+
+	if (setstack_hscx(st, hsp))
+		return (-1);
+
+	st->l2.extended = 0;
+	st->l2.laptype = LAPB;
+	st->l2.orig = !incoming;
+	st->l2.t200 = 1000;	/* 1000 milliseconds */
+	st->l2.window = 7;
+	st->l2.n200 = 4;	/* try 4 times       */
+	st->l2.t203 = 5000;	/* 5000 milliseconds */
+
+	st->l3.debug = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			sprintf(tmp, "Channel %d x.75", chanp->chan);
+			setstack_isdnl2(st, tmp);
+			st->l2.l2l3 = lldata_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l2.l2man = dcc_l2man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = NULL;
+			st->l4.l2writewakeup = ll_writewakeup;
+			st->l2.l2m.debug = chanp->debug & 16;
+			st->l2.debug = chanp->debug & 64;
+			st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+			st->l1.hscxmode = 2;	/* Packet-Mode ? */
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+			st->l1.l1l2 = lltrans_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = ll_writewakeup;
+			st->l1.hscxmode = 2;
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+		case (ISDN_PROTO_L2_TRANS):
+			st->l1.l1l2 = lltrans_handler;
+			st->l1.l1man = dcc_l1man;
+			st->l4.userdata = chanp;
+			st->l4.l1writewakeup = ll_writewakeup;
+			st->l1.hscxmode = 1;
+			st->l1.hscxchannel = chanp->para.bchannel - 1;
+			break;
+	}
+	return (0);
+}
+
+static void
+channel_report(struct Channel *chanp)
+{
+}
+
+static void
+distr_debug(struct IsdnCardState *csta, int debugflags)
+{
+	int i;
+	struct Channel *chanp = csta->channel;
+
+	for (i = 0; i < 2; i++) {
+		chanp[i].debug = debugflags;
+		chanp[i].fi.debug = debugflags & 2;
+		chanp[i].is.l2.l2m.debug = debugflags & 8;
+		chanp[i].ds.l2.l2m.debug = debugflags & 16;
+		chanp[i].is.l2.debug = debugflags & 32;
+		chanp[i].ds.l2.debug = debugflags & 64;
+		chanp[i].lc_d.lcfi.debug = debugflags & 128;
+		chanp[i].lc_b.lcfi.debug = debugflags & 256;
+	}
+	csta->dlogflag = debugflags & 4;
+	csta->teistack->l2.l2m.debug = debugflags & 512;
+}
+
+int
+HiSax_command(isdn_ctrl * ic)
+{
+	struct IsdnCardState *csta = hisax_findcard(ic->driver);
+	struct Channel *chanp;
+	char tmp[128];
+	int i;
+	unsigned int num;
+
+	if (!csta) {
+		printk(KERN_ERR
+		"HiSax: if_command %d called with invalid driverId %d!\n",
+		       ic->command, ic->driver);
+		return -ENODEV;
+	}
+	switch (ic->command) {
+		case (ISDN_CMD_SETEAZ):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "SETEAZ", 1);
+			break;
+		case (ISDN_CMD_SETL2):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1) {
+				sprintf(tmp, "SETL2 card %d %ld", csta->cardnr + 1,
+					ic->arg >> 8);
+				link_debug(chanp, tmp, 1);
+			}
+			chanp->l2_protocol = ic->arg >> 8;
+			break;
+		case (ISDN_CMD_DIAL):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1) {
+				sprintf(tmp, "DIAL %s -> %s (%d,%d)",
+					ic->parm.setup.eazmsn, ic->parm.setup.phone,
+					ic->parm.setup.si1, ic->parm.setup.si2);
+				link_debug(chanp, tmp, 1);
+			}
+			/* this solution is dirty and may be change, if
+			 * we make a callreference based callmanager */
+			if (chanp->fi.state == ST_NULL) {
+				FsmEvent(&chanp->fi, EV_DIAL, ic);
+			} else {
+				FsmDelTimer(&chanp->dial_timer, 70);
+				FsmAddTimer(&chanp->dial_timer, 50, EV_DIAL, ic, 71);
+			}
+			break;
+		case (ISDN_CMD_ACCEPTB):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "ACCEPTB", 1);
+			FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+			break;
+		case (ISDN_CMD_ACCEPTD):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "ACCEPTD", 1);
+			FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+			break;
+		case (ISDN_CMD_HANGUP):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, "HANGUP", 1);
+			FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+			break;
+		case (ISDN_CMD_SUSPEND):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1) {
+				sprintf(tmp, "SUSPEND %s", ic->parm.num);
+				link_debug(chanp, tmp, 1);
+			}
+			FsmEvent(&chanp->fi, EV_SUSPEND, ic);
+			break;
+		case (ISDN_CMD_RESUME):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1) {
+				sprintf(tmp, "RESUME %s", ic->parm.num);
+				link_debug(chanp, tmp, 1);
+			}
+			FsmEvent(&chanp->fi, EV_RESUME, ic);
+			break;
+		case (ISDN_CMD_LOCK):
+			HiSax_mod_inc_use_count();
+#ifdef MODULE
+			if (csta->channel[0].debug & 1024) {
+				jiftime(tmp, jiffies);
+				i = strlen(tmp);
+				sprintf(tmp + i, "   LOCK modcnt %lx\n", MOD_USE_COUNT);
+				HiSax_putstatus(csta, tmp);
+			}
+#endif				/* MODULE */
+			break;
+		case (ISDN_CMD_UNLOCK):
+			HiSax_mod_dec_use_count();
+#ifdef MODULE
+			if (csta->channel[0].debug & 1024) {
+				jiftime(tmp, jiffies);
+				i = strlen(tmp);
+				sprintf(tmp + i, " UNLOCK modcnt %lx\n", MOD_USE_COUNT);
+				HiSax_putstatus(csta, tmp);
+			}
+#endif				/* MODULE */
+			break;
+		case (ISDN_CMD_IOCTL):
+			switch (ic->arg) {
+				case (0):
+					HiSax_reportcard(csta->cardnr);
+					for (i = 0; i < 2; i++)
+						channel_report(&csta->channel[i]);
+					break;
+				case (1):
+					num = *(unsigned int *) ic->parm.num;
+					distr_debug(csta, num);
+					sprintf(tmp, "debugging flags card %d set to %x\n",
+						csta->cardnr + 1, num);
+					HiSax_putstatus(csta, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				case (2):
+					num = *(unsigned int *) ic->parm.num;
+					i = num >> 8;
+					if (i >= 2)
+						break;
+					chanp = csta->channel + i;
+					chanp->impair = num & 0xff;
+					if (chanp->debug & 1) {
+						sprintf(tmp, "IMPAIR %x", chanp->impair);
+						link_debug(chanp, tmp, 1);
+					}
+					break;
+				case (3):
+					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
+						HiSax_mod_dec_use_count();
+					break;
+				case (4):
+					for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
+						HiSax_mod_inc_use_count();
+					break;
+				case (5):	/* set card in leased mode */
+					csta->channel[0].leased = 1;
+					csta->channel[1].leased = 1;
+					sprintf(tmp, "card %d set into leased mode\n",
+						csta->cardnr + 1);
+					HiSax_putstatus(csta, tmp);
+					break;
+#ifdef MODULE
+				case (55):
+#if (LINUX_VERSION_CODE < 0x020111)
+					MOD_USE_COUNT = MOD_VISITED;
+#else
+					MOD_USE_COUNT = 0;
+#endif
+					HiSax_mod_inc_use_count();
+					break;
+#endif				/* MODULE */
+				case (11):
+					csta->debug = *(unsigned int *) ic->parm.num;
+					sprintf(tmp, "l1 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, csta->debug);
+					HiSax_putstatus(cards[0].sp, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				case (13):
+					csta->channel[0].is.l3.debug = *(unsigned int *) ic->parm.num;
+					csta->channel[1].is.l3.debug = *(unsigned int *) ic->parm.num;
+					sprintf(tmp, "l3 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, *(unsigned int *) ic->parm.num);
+					HiSax_putstatus(cards[0].sp, tmp);
+					printk(KERN_DEBUG "HiSax: %s", tmp);
+					break;
+				default:
+					printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
+					       (int) ic->arg);
+					return (-EINVAL);
+			}
+			break;
+		default:
+			break;
+	}
+
+	return (0);
+}
+
+int
+HiSax_writebuf(int id, int chan, const u_char * buf, int count, int user)
+{
+	struct IsdnCardState *csta = hisax_findcard(id);
+	struct Channel *chanp;
+	struct PStack *st;
+	struct BufHeader *ibh;
+	int err, i;
+	byte *ptr;
+	char tmp[64];
+
+	if (!csta) {
+		printk(KERN_ERR
+		    "HiSax: if_sendbuf called with invalid driverId!\n");
+		return -ENODEV;
+	}
+	chanp = csta->channel + chan;
+	st = &chanp->ds;
+	if (!chanp->data_open) {
+		link_debug(chanp, "writebuf: channel not open", 1);
+		return -EIO;
+	}
+	err = BufPoolGet(&ibh, st->l1.sbufpool, GFP_ATOMIC, st, 21);
+	if (err) {
+		/* Must return 0 here, since this is not an error
+		 * but a temporary lack of resources.
+		 */
+		if (chanp->debug & 1) {
+			sprintf(tmp, "writebuf: no buffers for %d bytes", count);
+			link_debug(chanp, tmp, 1);
+		}
+		return 0;
+	}
+#if 0
+	if (chanp->debug & 1) {
+		sprintf(tmp, "writebuf: %d bytes", count);
+		link_debug(chanp, tmp, 1);
+	}
+#endif
+	ptr = DATAPTR(ibh);
+	if (chanp->lc_b.l2_establish)
+		i = st->l2.ihsize;
+	else
+		i = 0;
+
+	if ((count + i) > BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS)) {
+		sprintf(tmp, "writebuf: packet too large (%d bytes)", count + i);
+		printk(KERN_WARNING "HiSax_%s !\n", tmp);
+		link_debug(chanp, tmp, 1);
+		return (-EINVAL);
+	}
+	ptr += i;
+
+	if (user)
+		copy_from_user(ptr, buf, count);
+	else
+		memcpy(ptr, buf, count);
+	ibh->datasize = count + i;
+
+	if (chanp->data_open) {
+		if (chanp->lc_b.l2_establish)
+			chanp->ds.l3.l3l2(&chanp->ds, DL_DATA, ibh);
+		else
+			chanp->ds.l2.l2l1(&chanp->ds, PH_DATA, ibh);
+		return (count);
+	} else {
+		BufPoolRelease(ibh);
+		return (0);
+	}
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/config.c linux/drivers/isdn/hisax/config.c
--- v2.1.26/linux/drivers/isdn/hisax/config.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/config.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,401 @@
+/* $Id: config.c,v 1.11 1997/02/14 12:23:12 fritz Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ *
+ * $Log: config.c,v $
+ * Revision 1.11  1997/02/14 12:23:12  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.10  1997/02/14 09:22:09  keil
+ * Final 2.0 version
+ *
+ * Revision 1.9  1997/02/10 11:45:09  fritz
+ * More changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.8  1997/02/09 00:28:05  keil
+ * new interface handling, one interface per card
+ * default protocol now works again
+ *
+ * Revision 1.7  1997/01/27 15:56:57  keil
+ * Teles PCMCIA ITK ix1 micro added
+ *
+ * Revision 1.6  1997/01/21 22:17:56  keil
+ * new module load syntax
+ *
+ * Revision 1.5  1997/01/09 18:28:20  keil
+ * cosmetic cleanups
+ *
+ * Revision 1.4  1996/11/05 19:35:17  keil
+ * using config.h; some spelling fixes
+ *
+ * Revision 1.3  1996/10/23 17:23:28  keil
+ * default config changes
+ *
+ * Revision 1.2  1996/10/23 11:58:48  fritz
+ * Changed default setup to reflect user's selection of supported
+ * cards/protocols.
+ *
+ * Revision 1.1  1996/10/13 20:04:51  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include "hisax.h"
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ *
+ * { type, protocol, p0, p1, p2, NULL }
+ *
+ * type
+ *    1 Teles 16.0      p0=irq p1=membase p2=iobase
+ *    2 Teles  8.0      p0=irq p1=membase
+ *    3 Teles 16.3      p0=irq p1=iobase
+ *    4 Creatix PNP     p0=irq p1=IO0 (ISAC)  p2=IO1 (HSCX)
+ *    5 AVM A1 (Fritz)  p0=irq p1=iobase
+ *    6 ELSA PC         [p0=iobase] or nothing (autodetect)
+ *    7 ELSA Quickstep  p0=irq p1=iobase
+ *    8 Teles PCMCIA    p0=irq p1=iobase
+ *    9 ITK ix1-micro   p0=irq p1=iobase
+ *
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6
+ *
+ *
+ */
+
+#ifdef CONFIG_HISAX_ELSA_PCC
+#define DEFAULT_CARD ISDN_CTYPE_ELSA
+#define DEFAULT_CFG {0,0,0}
+#endif
+#ifdef CONFIG_HISAX_AVM_A1
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1
+#define DEFAULT_CFG {10,0x340,0}
+#endif
+#ifdef CONFIG_HISAX_16_3
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_3
+#define DEFAULT_CFG {15,0x180,0}
+#endif
+#ifdef CONFIG_HISAX_16_0
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_0
+#define DEFAULT_CFG {15,0xd0000,0xd80}
+#endif
+
+#ifdef CONFIG_HISAX_IX1MICROR2
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2
+#define DEFAULT_CFG {5,0x390,0}
+#endif
+
+#ifdef CONFIG_HISAX_1TR6
+#define DEFAULT_PROTO ISDN_PTYPE_1TR6
+#define DEFAULT_PROTO_NAME "1TR6"
+#endif
+#ifdef CONFIG_HISAX_EURO
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_EURO
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "EURO"
+#endif
+#ifndef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN
+#define DEFAULT_PROTO_NAME "UNKNOWN"
+#endif
+#ifndef DEFAULT_CARD
+#error "HiSax: No cards configured"
+#endif
+
+#define FIRST_CARD { \
+  DEFAULT_CARD, \
+  DEFAULT_PROTO, \
+  DEFAULT_CFG, \
+  NULL, \
+}
+
+#define EMPTY_CARD	{0, DEFAULT_PROTO, {0, 0, 0}, NULL}
+
+struct IsdnCard cards[] =
+{
+	FIRST_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+	EMPTY_CARD,
+};
+
+static char HiSaxID[96] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+char *HiSax_id = HiSaxID;
+#ifdef MODULE
+/* Variables for insmod */
+int type[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int protocol[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int io[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+int io0[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int io1[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#endif
+int irq[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+int mem[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+char *id = HiSaxID;
+
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_AUTHOR("Karsten Keil");
+MODULE_PARM(type, "1-16i");
+MODULE_PARM(protocol, "1-16i");
+MODULE_PARM(io, "1-16i");
+MODULE_PARM(irq, "1-16i");
+MODULE_PARM(mem, "1-16i");
+MODULE_PARM(id, "s");
+#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+MODULE_PARM(io0, "1-16i");
+MODULE_PARM(io1, "1-16i");
+#endif
+#endif
+
+#endif
+
+extern char *l1_revision;
+extern char *l2_revision;
+extern char *l3_revision;
+extern char *l4_revision;
+extern char *tei_revision;
+
+char *
+HiSax_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+int nrcards;
+
+void
+HiSax_mod_dec_use_count(void)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+void
+HiSax_mod_inc_use_count(void)
+{
+	MOD_INC_USE_COUNT;
+}
+
+#ifdef MODULE
+#define HiSax_init init_module
+#else
+void
+HiSax_setup(char *str, int *ints)
+{
+	int i, j, argc;
+
+	argc = ints[0];
+	i = 0;
+	j = 1;
+	while (argc && (i < 16)) {
+		if (argc) {
+			cards[i].typ = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].protocol = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[0] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[1] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[2] = ints[j];
+			j++;
+			argc--;
+		}
+		i++;
+	}
+	if (strlen(str)) {
+		strcpy(HiSaxID, str);
+		HiSax_id = HiSaxID;
+	} else {
+		strcpy(HiSaxID, "HiSax");
+		HiSax_id = HiSaxID;
+	}
+}
+#endif
+
+int
+HiSax_init(void)
+{
+	int i;
+	char tmp[64], rev[64];
+	char *r = rev;
+	int nzproto = 0;
+
+	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.0\n");
+	printk(KERN_NOTICE "HiSax: Revisions %s\n", rev);
+
+#ifdef MODULE
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	for (i = 0; i < 16; i++) {
+		cards[i].typ = type[i];
+		if (protocol[i]) {
+			cards[i].protocol = protocol[i];
+			nzproto++;
+		}
+		switch (type[i]) {
+			case ISDN_CTYPE_16_0:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = mem[i];
+				cards[i].para[2] = io[i];
+				break;
+
+			case ISDN_CTYPE_8_0:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = mem[i];
+				break;
+
+			case ISDN_CTYPE_16_3:
+			case ISDN_CTYPE_TELESPCMCIA:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+#ifdef CONFIG_HISAX_16_3	/* For Creatix/Teles PnP */
+			case ISDN_CTYPE_PNP:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io0[i];
+				cards[i].para[2] = io1[i];
+				break;
+#endif
+			case ISDN_CTYPE_A1:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+			case ISDN_CTYPE_ELSA:
+				cards[i].para[0] = io[i];
+				break;
+			case ISDN_CTYPE_ELSA_QS1000:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+			case ISDN_CTYPE_IX1MICROR2:
+				cards[i].para[0] = irq[i];
+				cards[i].para[1] = io[i];
+				break;
+
+		}
+	}
+	if (!nzproto) {
+		printk(KERN_WARNING "HiSax: Warning - no protocol specified\n");
+		printk(KERN_WARNING "HiSax: Note! module load syntax has changed.\n");
+		printk(KERN_WARNING "HiSax: using protocol %s\n", DEFAULT_PROTO_NAME);
+	}
+#endif
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < 16; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	CallcNew();
+	Isdnl2New();
+	if (HiSax_inithardware()) {
+		/* Install only, if at least one card found */
+		/* No symbols to export, hide all symbols */
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE < 0x020111)
+		register_symtab(NULL);
+#else
+		EXPORT_NO_SYMBOLS;
+#endif
+		printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+		return (0);
+	} else {
+		Isdnl2Free();
+		CallcFree();
+		return -EIO;
+	}
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+	HiSax_closehardware();
+	printk(KERN_NOTICE "HiSax module removed\n");
+}
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/elsa.c linux/drivers/isdn/hisax/elsa.c
--- v2.1.26/linux/drivers/isdn/hisax/elsa.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/elsa.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,1327 @@
+/* $Id: elsa.c,v 1.8 1997/01/27 15:51:48 keil Exp $
+
+ * elsa.c     low level stuff for Elsa isdn cards
+ *
+ * Author     Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Elsa GmbH for documents and informations
+ *
+ *
+ * $Log: elsa.c,v $
+ * Revision 1.8  1997/01/27 15:51:48  keil
+ * SMP proof,cosmetics
+ *
+ * Revision 1.7  1997/01/21 22:20:48  keil
+ * Elsa Quickstep support
+ *
+ * Revision 1.6  1997/01/09 18:22:46  keil
+ * one more PCC-8 fix
+ *
+ * Revision 1.5  1996/12/08 19:46:14  keil
+ * PCC-8 correct IRQs; starting ARCOFI support
+ *
+ * Revision 1.4  1996/11/18 20:50:54  keil
+ * with PCF Pro release 16 Byte IO
+ *
+ * Revision 1.3  1996/11/18 15:33:04  keil
+ * PCC and PCFPro support
+ *
+ * Revision 1.2  1996/10/27 22:08:03  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:52  keil
+ * Initial revision
+ *
+ *
+ */
+
+#define ARCOFI_USE	0
+
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "elsa.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+
+const char *Elsa_revision = "$Revision: 1.8 $";
+const char *Elsa_Types[] =
+{"None", "PCC-8", "PCF-Pro", "PCC-16", "PCF",
+ "QS 1000"};
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline byte
+readhscx(unsigned int adr, int hscx, byte off)
+{
+	register byte 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, byte * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(adr + CARD_ALE, (hscx ? 0x40 : 0));
+	insb(adr + CARD_HSCX, data, size);
+}
+
+
+static inline void
+writehscx(unsigned int adr, int hscx, byte off, byte data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off + (hscx ? 0x60 : 0x20));
+	byteout(adr + CARD_HSCX, data);
+	restore_flags(flags);
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, byte * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+	byteout(adr + CARD_ALE, (hscx ? 0x40 : 0));
+	outsb(adr + CARD_HSCX, data, size);
+}
+
+static inline byte
+readisac(unsigned int adr, byte off)
+{
+	register byte ret;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off + 0x20);
+	ret = bytein(adr);
+	restore_flags(flags);
+	return (ret);
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, byte * data, int size)
+{
+	/* fifo read without cli because it's allready done  */
+
+	byteout(adr + CARD_ALE, 0);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writeisac(unsigned int adr, byte off, byte data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	byteout(adr + CARD_ALE, off + 0x20);
+	byteout(adr, data);
+	restore_flags(flags);
+}
+
+static inline void
+write_fifo_isac(unsigned int adr, byte * data, int size)
+{
+	/* fifo write without cli because it's allready done  */
+
+	byteout(adr + CARD_ALE, 0);
+	outsb(adr, data, size);
+}
+
+static inline int
+TimerRun(struct IsdnCardState *sp)
+{
+	register byte val;
+
+	val = bytein(sp->cfg_reg + CARD_CONFIG);
+	if (sp->subtyp == ELSA_QS1000)
+		return (0 == (val & TIMER_RUN));
+	return ((val & TIMER_RUN));
+}
+
+static inline void
+elsa_led_handler(struct IsdnCardState *sp)
+{
+
+	byte 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);
+}
+
+static inline void
+waitforCEC(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Elsa: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Elsa: waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR(int adr, int hscx, byte data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr, hscx);
+	writehscx(adr, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->cfg_reg, hscx, HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->cfg_reg, hscx, HSCX_EXIR));
+}
+
+void
+elsa_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readisac(sp->cfg_reg, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readisac(sp->cfg_reg, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readisac(sp->cfg_reg, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	byte *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh = hsp->rcvibh;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+					      HSCX_RBUF_BPPS)) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->rcvptr;
+
+	hsp->rcvptr += count;
+	save_flags(flags);
+	cli();
+	read_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count);
+	writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh;
+	int more, count;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	ibh = hsp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - hsp->sendptr;
+	if (count <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->sendptr;
+	hsp->sendptr += count;
+
+	waitforXFW(sp->cfg_reg, hsp->hscx);
+	save_flags(flags);
+	cli();
+	write_fifo_hscx(sp->cfg_reg, hsp->hscx, ptr, count);
+	writehscxCMDR(sp->cfg_reg, hsp->hscx, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, byte val, byte hscx)
+{
+	byte r;
+	struct HscxState *hsp = sp->hs + hscx;
+	int count, err;
+	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");
+			if (hsp->rcvibh)
+				BufPoolRelease(hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+			goto afterRME;
+		}
+		if (!hsp->rcvibh)
+			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+				       GFP_ATOMIC, (void *) 1, 1)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RME out of buffers");
+				writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+				goto afterRME;
+			} else
+				hsp->rcvptr = 0;
+
+		count = readhscx(sp->cfg_reg, hsp->hscx, HSCX_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		hscx_empty_fifo(hsp, count);
+		hsp->rcvibh->datasize = hsp->rcvptr - 1;
+		BufQueueLink(&hsp->rq, hsp->rcvibh);
+		hsp->rcvibh = NULL;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!hsp->rcvibh) {
+			if (hsp->mode == 1)
+				err = BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+			else
+				err = BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+
+			if (err) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RPF out of buffers");
+				writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x80);
+				goto afterRPF;
+			} else
+				hsp->rcvptr = 0;
+		}
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			hsp->rcvibh->datasize = hsp->rcvptr;
+			BufQueueLink(&hsp->rq, hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+      afterRPF:
+	if (val & 0x10) {	/* XPR */
+		if (hsp->xmtibh)
+			if (hsp->xmtibh->datasize > hsp->sendptr) {
+				hscx_fill_fifo(hsp);
+				goto afterXPR;
+			} else {
+				if (hsp->releasebuf)
+					BufPoolRelease(hsp->xmtibh);
+				hsp->sendptr = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->xmtibh = NULL;
+			}
+		if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+			hsp->releasebuf = !0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+      afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	byte *ptr;
+	struct BufHeader *ibh = sp->rcvibh;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_empty_fifo");
+
+	if (sp->rcvptr >= 3072) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo rcvptr %d", sp->rcvptr);
+			debugl1(sp, tmp);
+		}
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->rcvptr;
+	sp->rcvptr += count;
+
+	save_flags(flags);
+	cli();
+	read_fifo_isac(sp->cfg_reg, ptr, count);
+	writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh;
+	int count, more;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	ibh = sp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - sp->sendptr;
+	if (count <= 0)
+		return;
+	if (count >= 3072)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->sendptr;
+	sp->sendptr += count;
+
+	save_flags(flags);
+	cli();
+	write_fifo_isac(sp->cfg_reg, ptr, count);
+	writeisac(sp->cfg_reg, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	writeisac(sp->cfg_reg, ISAC_CIX0, (command << 2) | 3);
+}
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, byte val)
+{
+	byte exval, v1;
+	unsigned int count;
+	char tmp[32];
+#if ARCOFI_USE
+	struct BufHeader *ibh;
+	byte *ptr;
+#endif
+
+	if (sp->debug & L1_DEB_ISAC) {
+		sprintf(tmp, "ISAC interrupt %x", val);
+		debugl1(sp, tmp);
+	}
+	if (val & 0x80) {	/* RME */
+		exval = readisac(sp->cfg_reg, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40)
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RDO");
+			if (!exval & 0x20)
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC CRC error");
+			if (sp->rcvibh)
+				BufPoolRelease(sp->rcvibh);
+			sp->rcvibh = NULL;
+			writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
+			goto afterRME;
+		}
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 3)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
+				goto afterRME;
+			} else
+				sp->rcvptr = 0;
+		count = readisac(sp->cfg_reg, ISAC_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		isac_empty_fifo(sp, count);
+		sp->rcvibh->datasize = sp->rcvptr;
+		BufQueueLink(&(sp->rq), sp->rcvibh);
+		sp->rcvibh = NULL;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 4)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writeisac(sp->cfg_reg, ISAC_CMDR, 0x80);
+				goto afterRPF;
+			} else
+				sp->rcvptr = 0;
+		isac_empty_fifo(sp, 32);
+	}
+      afterRPF:
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->xmtibh)
+			if (sp->xmtibh->datasize > sp->sendptr) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				if (sp->releasebuf)
+					BufPoolRelease(sp->xmtibh);
+				sp->xmtibh = NULL;
+				sp->sendptr = 0;
+			}
+		if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+			sp->releasebuf = !0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (readisac(sp->cfg_reg, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = readisac(sp->cfg_reg, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+		if (exval & 0x08) {
+			v1 = readisac(sp->cfg_reg, ISAC_MOSR);
+			if (sp->debug & L1_DEB_WARN) {
+				sprintf(tmp, "ISAC MOSR %02x", v1);
+				debugl1(sp, tmp);
+			}
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!sp->mon_rx)
+					if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool),
+					    GFP_ATOMIC, (void *) 1, 3)) {
+						if (sp->debug & L1_DEB_WARN)
+							debugl1(sp, "ISAC MON RX out of buffers!");
+						writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+						goto afterMONR0;
+					} else
+						sp->mon_rxp = 0;
+				ibh = sp->mon_rx;
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_rxp;
+				sp->mon_rxp++;
+				if (sp->mon_rxp >= 3072) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+					sp->mon_rxp = 0;
+					if (sp->debug & L1_DEB_WARN)
+						debugl1(sp, "ISAC MON RX overflow!");
+					goto afterMONR0;
+				}
+				*ptr = readisac(sp->cfg_reg, ISAC_MOR0);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC MOR0 %02x", *ptr);
+					debugl1(sp, tmp);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!sp->mon_rx)
+					if (BufPoolGet(&(sp->mon_rx), &(sp->rbufpool),
+					    GFP_ATOMIC, (void *) 1, 3)) {
+						if (sp->debug & L1_DEB_WARN)
+							debugl1(sp, "ISAC MON RX out of buffers!");
+						writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+						goto afterMONR1;
+					} else
+						sp->mon_rxp = 0;
+				ibh = sp->mon_rx;
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_rxp;
+				sp->mon_rxp++;
+				if (sp->mon_rxp >= 3072) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+					sp->mon_rxp = 0;
+					if (sp->debug & L1_DEB_WARN)
+						debugl1(sp, "ISAC MON RX overflow!");
+					goto afterMONR1;
+				}
+				*ptr = readisac(sp->cfg_reg, ISAC_MOR1);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "ISAC MOR1 %02x", *ptr);
+					debugl1(sp, tmp);
+				}
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+				sp->mon_rx->datasize = sp->mon_rxp;
+				sp->mon_flg |= MON0_RX;
+			}
+			if (v1 & 0x40) {
+				writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+				sp->mon_rx->datasize = sp->mon_rxp;
+				sp->mon_flg |= MON1_RX;
+			}
+			if (v1 == 0x02) {
+				ibh = sp->mon_tx;
+				if (!ibh) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0a);
+					goto AfterMOX0;
+				}
+				count = ibh->datasize - sp->mon_txp;
+				if (count <= 0) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0x0f);
+					BufPoolRelease(sp->mon_tx);
+					sp->mon_tx = NULL;
+					sp->mon_txp = 0;
+					sp->mon_flg |= MON0_TX;
+					goto AfterMOX0;
+				}
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_txp;
+				sp->mon_txp++;
+				writeisac(sp->cfg_reg, ISAC_MOX0, *ptr);
+			}
+		      AfterMOX0:
+			if (v1 == 0x20) {
+				ibh = sp->mon_tx;
+				if (!ibh) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+					goto AfterMOX1;
+				}
+				count = ibh->datasize - sp->mon_txp;
+				if (count <= 0) {
+					writeisac(sp->cfg_reg, ISAC_MOCR, 0xf0);
+					BufPoolRelease(sp->mon_tx);
+					sp->mon_tx = NULL;
+					sp->mon_txp = 0;
+					sp->mon_flg |= MON1_TX;
+					goto AfterMOX1;
+				}
+				ptr = DATAPTR(ibh);
+				ptr += sp->mon_txp;
+				sp->mon_txp++;
+				writeisac(sp->cfg_reg, ISAC_MOX1, *ptr);
+			}
+		      AfterMOX1:
+#endif
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, byte val)
+{
+
+	byte 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.
+				 */
+				hsp->sendptr = 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.
+				 */
+				hsp->sendptr = 0;
+				writehscxCMDR(sp->cfg_reg, hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readhscx(sp->cfg_reg, 0, HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	byte val, sval, stat = 0;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
+		return;
+	}
+	sval = bytein(sp->cfg_reg + CARD_CONFIG);
+      INT_RESTART:
+	if (!TimerRun(sp)) {
+		/* Timer Restart */
+		bytein(sp->cfg_reg + CARD_START_TIMER);
+		if (!(sp->counter++ & 0x3f)) {
+			/* Call LEDs all 64 tics */
+			elsa_led_handler(sp);
+		}
+	}
+	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+		stat |= 1;
+	}
+	val = readisac(sp->cfg_reg, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+		stat |= 2;
+	}
+	sval = bytein(sp->cfg_reg + CARD_CONFIG);
+	if (!TimerRun(sp))
+		goto INT_RESTART;
+
+	val = readhscx(sp->cfg_reg, 1, HSCX_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readisac(sp->cfg_reg, ISAC_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writehscx(sp->cfg_reg, 0, HSCX_MASK, 0xFF);
+		writehscx(sp->cfg_reg, 1, HSCX_MASK, 0xFF);
+		writehscx(sp->cfg_reg, 0, HSCX_MASK, 0x0);
+		writehscx(sp->cfg_reg, 1, HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writeisac(sp->cfg_reg, ISAC_MASK, 0xFF);
+		writeisac(sp->cfg_reg, ISAC_MASK, 0x0);
+	}
+	byteout(sp->cfg_reg + 7, 0xff);
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->cfg_reg;
+
+	/* Elsa IOM 2 Mode */
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_ADF2, 0x80);
+	writeisac(adr, ISAC_SQXR, 0x2f);
+	writeisac(adr, ISAC_SPCR, 0x00);
+	writeisac(adr, ISAC_STCR, 0x70);
+	writeisac(adr, ISAC_MODE, 0xc9);
+	writeisac(adr, ISAC_TIMR, 0x00);
+	writeisac(adr, ISAC_ADF1, 0x00);
+	writeisac(adr, ISAC_CIX0, (1 << 2) | 3);
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writehscx(sp->cfg_reg, hscx, HSCX_CCR1, 0x85);
+	writehscx(sp->cfg_reg, hscx, HSCX_XAD1, 0xFF);
+	writehscx(sp->cfg_reg, hscx, HSCX_XAD2, 0xFF);
+	writehscx(sp->cfg_reg, hscx, HSCX_RAH2, 0xFF);
+	writehscx(sp->cfg_reg, hscx, HSCX_XBCH, 0x0);
+	writehscx(sp->cfg_reg, hscx, HSCX_RLCR, 0x0);
+	writehscx(sp->cfg_reg, hscx, HSCX_CCR2, 0x30);
+
+	switch (mode) {
+	case (0):
+		writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0xff);
+		writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0xff);
+		writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
+		writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
+		writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x84);
+		break;
+	case (1):
+		if (ichan == 0) {
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f);
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f);
+		} else {
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3);
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3);
+		}
+		writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
+		writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
+		writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0xe4);
+		writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41);
+		break;
+	case (2):
+		if (ichan == 0) {
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x2f);
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x2f);
+		} else {
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAX, 0x3);
+			writehscx(sp->cfg_reg, hscx, HSCX_TSAR, 0x3);
+		}
+		writehscx(sp->cfg_reg, hscx, HSCX_XCCR, 7);
+		writehscx(sp->cfg_reg, hscx, HSCX_RCCR, 7);
+		writehscx(sp->cfg_reg, hscx, HSCX_MODE, 0x8c);
+		writehscx(sp->cfg_reg, hscx, HSCX_CMDR, 0x41);
+		break;
+	}
+	writehscx(sp->cfg_reg, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+release_io_elsa(struct IsdnCard *card)
+{
+	int bytecnt = 8;
+
+	if (card->sp->subtyp == ELSA_PCFPRO)
+		bytecnt = 16;
+	if (card->sp->cfg_reg)
+		release_region(card->sp->cfg_reg, bytecnt);
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	writeisac(sp->cfg_reg, ISAC_MASK, 0);
+	writeisac(sp->cfg_reg, ISAC_CMDR, 0x41);
+}
+
+static void
+check_arcofi(struct IsdnCardState *sp)
+{
+#if 0
+	byte val;
+	char tmp[40];
+	char *t;
+	long flags;
+	byte *p;
+
+	if (BufPoolGet(&(sp->mon_tx), &(sp->sbufpool),
+		       GFP_ATOMIC, (void *) 1, 3)) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC MON TX out of buffers!");
+		return;
+	} else
+		sp->mon_txp = 0;
+	p = DATAPTR(sp->mon_tx);
+	*p++ = 0xa0;
+	*p++ = 0x0;
+	sp->mon_tx->datasize = 2;
+	sp->mon_txp = 1;
+	sp->mon_flg = 0;
+	writeisac(sp->cfg_reg, ISAC_MOCR, 0xa0);
+	val = readisac(sp->cfg_reg, ISAC_MOSR);
+	writeisac(sp->cfg_reg, ISAC_MOX1, 0xa0);
+	writeisac(sp->cfg_reg, ISAC_MOCR, 0xb0);
+	save_flags(flags);
+	sti();
+	HZDELAY(3);
+	restore_flags(flags);
+	if (sp->mon_flg & MON1_TX) {
+		if (sp->mon_flg & MON1_RX) {
+			sprintf(tmp, "Arcofi response received %d bytes", sp->mon_rx->datasize);
+			debugl1(sp, tmp);
+			p = DATAPTR(sp->mon_rx);
+			t = tmp;
+			t += sprintf(tmp, "Arcofi data");
+			QuickHex(t, p, sp->mon_rx->datasize);
+			debugl1(sp, tmp);
+			BufPoolRelease(sp->mon_rx);
+			sp->mon_rx = NULL;
+			sp->mon_rxp = 0;
+			sp->mon_flg = 0;
+		}
+	} else if (sp->mon_tx) {
+		BufPoolRelease(sp->mon_tx);
+		sp->mon_tx = NULL;
+		sp->mon_txp = 0;
+		sprintf(tmp, "Arcofi not detected");
+		debugl1(sp, tmp);
+	}
+	sp->mon_flg = 0;
+#endif
+}
+
+int
+initelsa(struct IsdnCardState *sp)
+{
+	int ret, irq_cnt;
+	long flags;
+
+	sp->counter = 0;
+	irq_cnt = kstat.interrupts[sp->irq];
+	printk(KERN_INFO "Elsa: IRQ %d count %d\n", sp->irq, irq_cnt);
+	clear_pending_ints(sp);
+	ret = get_irq(sp->cardnr, &elsa_interrupt);
+	if (ret) {
+		initisac(sp);
+		sp->modehscx(sp->hs, 0, 0);
+		sp->modehscx(sp->hs + 1, 0, 0);
+		save_flags(flags);
+		sp->counter = 0;
+		sti();
+		byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET | ENABLE_TIM_INT);
+		bytein(sp->cfg_reg + CARD_START_TIMER);
+		HZDELAY(11);	/* Warte 110 ms */
+		restore_flags(flags);
+		printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+		       sp->counter);
+		if (abs(sp->counter - 12) < 3) {
+			printk(KERN_INFO "Elsa: timer and irq OK\n");
+		} else {
+			printk(KERN_WARNING
+			"Elsa: timer problem maybe an IRQ(%d) conflict\n",
+			       sp->irq);
+		}
+		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\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+		check_arcofi(sp);
+	}
+	sp->counter = 0;
+	return (ret);
+}
+
+static unsigned char
+probe_elsa_adr(unsigned int adr)
+{
+	int i, in1, in2, p16_1 = 0, p16_2 = 0, pcc_1 = 0, pcc_2 = 0,
+	 pfp_1 = 0, pfp_2 = 0;
+	long flags;
+
+	if (check_region(adr, 8)) {
+		printk(KERN_WARNING
+		       "Elsa: Probing Port 0x%x: already in use\n",
+		       adr);
+		return (0);
+	}
+	save_flags(flags);
+	cli();
+	for (i = 0; i < 16; i++) {
+		in1 = inb(adr + CARD_CONFIG);	/* 'toggelt' bei */
+		in2 = inb(adr + CARD_CONFIG);	/* jedem Zugriff */
+		p16_1 += 0x04 & in1;
+		p16_2 += 0x04 & in2;
+		pcc_1 += 0x01 & in1;
+		pcc_2 += 0x01 & in2;
+		pfp_1 += 0x40 & in1;
+		pfp_2 += 0x40 & in2;
+	}
+	restore_flags(flags);
+	printk(KERN_INFO "Elsa: Probing IO 0x%x", adr);
+	if (65 == ++p16_1 * ++p16_2) {
+		printk(" PCC-16/PCF found\n");
+		return (3);
+	} else if (1025 == ++pfp_1 * ++pfp_2) {
+		printk(" PCF-Pro found\n");
+		return (2);
+	} else if (17 == ++pcc_1 * ++pcc_2) {
+		printk(" PCC8 found\n");
+		return (1);
+	} else {
+		printk(" failed\n");
+		return (0);
+	}
+}
+
+static unsigned int
+probe_elsa(struct IsdnCardState *sp)
+{
+	int i;
+	unsigned int CARD_portlist[] =
+	{0x160, 0x170, 0x260, 0x360, 0};
+
+	for (i = 0; CARD_portlist[i]; i++) {
+		if ((sp->subtyp = probe_elsa_adr(CARD_portlist[i])))
+			break;
+	}
+	return (CARD_portlist[i]);
+}
+
+int
+setup_elsa(struct IsdnCard *card)
+{
+	long flags;
+	int bytecnt;
+	byte val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	char tmp[64];
+
+	strcpy(tmp, Elsa_revision);
+	printk(KERN_NOTICE "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
+	if (sp->typ == ISDN_CTYPE_ELSA) {
+		sp->cfg_reg = card->para[0];
+		printk(KERN_INFO "Elsa: Mircolink IO probing\n");
+		if (sp->cfg_reg) {
+			if (!(sp->subtyp = probe_elsa_adr(sp->cfg_reg))) {
+				printk(KERN_WARNING
+				     "Elsa: no Elsa Mircolink at 0x%x\n",
+				       sp->cfg_reg);
+				return (0);
+			}
+		} else
+			sp->cfg_reg = probe_elsa(sp);
+		if (sp->cfg_reg) {
+			val = bytein(sp->cfg_reg + CARD_CONFIG);
+			if (sp->subtyp == ELSA_PCC) {
+				const byte CARD_IrqTab[8] =
+				{7, 3, 5, 9, 0, 0, 0, 0};
+				sp->irq = CARD_IrqTab[(val & 0x0c) >> 2];
+			} else {
+				const byte CARD_IrqTab[8] =
+				{15, 10, 15, 3, 11, 5, 11, 9};
+				sp->irq = CARD_IrqTab[(val & 0x38) >> 3];
+			}
+			val = bytein(sp->cfg_reg + CARD_ALE) & 0x7;
+			if (val < 3)
+				val |= 8;
+			val += 'A' - 3;
+			if (val == 'B' || val == 'C')
+				val ^= 1;
+			if ((sp->subtyp == ELSA_PCFPRO) && (val = 'G'))
+				val = 'C';
+			printk(KERN_INFO
+			       "Elsa: %s found at 0x%x Rev.:%c IRQ %d\n",
+			       Elsa_Types[sp->subtyp],
+			       sp->cfg_reg,
+			       val, sp->irq);
+			val = bytein(sp->cfg_reg + CARD_ALE) & 0x08;
+			if (val)
+				printk(KERN_WARNING
+				   "Elsa: Mircolink S0 bus power bad\n");
+		} else {
+			printk(KERN_WARNING
+			       "No Elsa Mircolink 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
+		return (0);
+
+	switch (sp->subtyp) {
+	case ELSA_PCC:
+		bytecnt = 8;
+		break;
+	case ELSA_PCFPRO:
+		bytecnt = 16;
+		break;
+	case ELSA_PCC16:
+		bytecnt = 8;
+		break;
+	case ELSA_PCF:
+		bytecnt = 16;
+		break;
+	case ELSA_QS1000:
+		bytecnt = 8;
+		break;
+	default:
+		printk(KERN_WARNING
+		       "Unknown ELSA subtype %d\n", sp->subtyp);
+		return (0);
+	}
+
+	if (check_region((sp->cfg_reg), bytecnt)) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       sp->cfg_reg,
+		       sp->cfg_reg + bytecnt);
+		return (0);
+	} else {
+		request_region(sp->cfg_reg, bytecnt, "elsa isdn");
+	}
+
+	/* Teste Timer */
+	bytein(sp->cfg_reg + CARD_START_TIMER);
+	if (!TimerRun(sp)) {
+		bytein(sp->cfg_reg + CARD_START_TIMER);		/* 2. Versuch */
+		if (!TimerRun(sp)) {
+			printk(KERN_WARNING
+			       "Elsa: timer do not start\n");
+			release_io_elsa(card);
+			return (0);
+		}
+	}
+	save_flags(flags);
+	sti();
+	HZDELAY(1);		/* wait >=10 ms */
+	restore_flags(flags);
+	if (TimerRun(sp)) {
+		printk(KERN_WARNING "Elsa: timer do not run down\n");
+		release_io_elsa(card);
+		return (0);
+	}
+	printk(KERN_INFO "Elsa: timer OK; resetting card\n");
+	/* Wait 1 Timer */
+	bytein(sp->cfg_reg + CARD_START_TIMER);
+	while (TimerRun(sp));
+	byteout(sp->cfg_reg + CARD_CONTROL, 0x00);	/* Reset On */
+	/* Wait 1 Timer */
+	bytein(sp->cfg_reg + CARD_START_TIMER);
+	while (TimerRun(sp));
+	byteout(sp->cfg_reg + CARD_CONTROL, ISDN_RESET);	/* Reset Off */
+	/* Wait 1 Timer */
+	bytein(sp->cfg_reg + CARD_START_TIMER);
+	while (TimerRun(sp));
+
+	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));
+
+	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.1.26/linux/drivers/isdn/hisax/elsa.h linux/drivers/isdn/hisax/elsa.h
--- v2.1.26/linux/drivers/isdn/hisax/elsa.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/elsa.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,63 @@
+/* $Id: elsa.h,v 1.4 1997/01/21 22:21:05 keil Exp $
+ *
+ * elsa.h   Header for Elsa ISDN cards
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Elsa GmbH for documents and informations
+ *
+ *
+ * $Log: elsa.h,v $
+ * Revision 1.4  1997/01/21 22:21:05  keil
+ * Elsa Quickstep support
+ *
+ * Revision 1.3  1996/12/08 19:47:38  keil
+ * ARCOFI support
+ *
+ * Revision 1.2  1996/11/18 15:33:35  keil
+ * PCC and PCFPro support
+ *
+ * Revision 1.1  1996/10/13 20:03:45  keil
+ * Initial revision
+ *
+ *
+*/
+
+#define CARD_ISAC	0
+#define CARD_HSCX	2
+#define CARD_ALE	3
+#define CARD_CONTROL	4
+#define CARD_CONFIG	5
+#define CARD_START_TIMER 6
+#define CARD_TRIG_IRQ	7
+
+#define ELSA_PCC     1
+#define ELSA_PCFPRO  2
+#define ELSA_PCC16   3
+#define ELSA_PCF     4
+#define ELSA_QS1000  5
+
+/***                                                                    ***
+ ***   Makros als Befehle fuer die Kartenregister                       ***
+ ***   (mehrere Befehle werden durch Bit-Oderung kombiniert)            ***
+ ***                                                                    ***/
+
+/* Config-Register (Read) */
+#define TIMER_RUN       0x02    /* Bit 1 des Config-Reg     */
+#define TIMER_RUN_PCC   0x01    /* Bit 0 des Config-Reg  bei PCC */
+#define IRQ_INDEX       0x38    /* Bit 3,4,5 des Config-Reg */
+
+/* Control-Register (Write) */
+#define LINE_LED        0x02    /* Bit 1 Gelbe LED */
+#define STAT_LED        0x08    /* Bit 3 Gruene LED */
+#define ISDN_RESET      0x20    /* Bit 5 Reset-Leitung */
+#define ENABLE_TIM_INT  0x80    /* Bit 7 Freigabe Timer Interrupt */
+
+/* ALE-Register (Read) */
+#define HW_RELEASE      0x07    /* Bit 0-2 Hardwarerkennung */
+#define S0_POWER_BAD    0x08    /* Bit 3 S0-Bus Spannung fehlt */
+
+extern	void elsa_report(struct IsdnCardState *sp);
+extern  void release_io_elsa(struct IsdnCard *card);
+extern	int  setup_elsa(struct IsdnCard *card);
+extern  int  initelsa(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/fsm.c linux/drivers/isdn/hisax/fsm.c
--- v2.1.26/linux/drivers/isdn/hisax/fsm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/fsm.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,180 @@
+/* $Id: fsm.c,v 1.3 1997/02/16 01:04:08 fritz Exp $
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: fsm.c,v $
+ * Revision 1.3  1997/02/16 01:04:08  fritz
+ * Bugfix: Changed timer handling caused hang with 2.1.X
+ *
+ * Revision 1.2  1997/01/09 20:57:27  keil
+ * cleanup & FSM_TIMER_DEBUG
+ *
+ * Revision 1.1  1996/10/13 20:04:52  keil
+ * Initial revision
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+
+#define FSM_TIMER_DEBUG 0
+
+void
+FsmNew(struct Fsm *fsm,
+       struct FsmNode *fnlist, int fncount)
+{
+	int             i;
+
+	fsm->jumpmatrix = (int *) Smalloc(4L * fsm->state_count * fsm->event_count,
+					  GFP_KERNEL, "Fsm jumpmatrix");
+	memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
+
+	for (i = 0; i < fncount; i++)
+		fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+			      fnlist[i].state] = (int) fnlist[i].routine;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+	Sfree((void *) fsm->jumpmatrix);
+}
+
+int
+FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+	void            (*r) (struct FsmInst *, int, void *);
+	char            str[80];
+
+	r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+	if (r) {
+		if (fi->debug) {
+			sprintf(str, "State %s Event %s",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+			fi->printdebug(fi, str);
+		}
+		r(fi, event, arg);
+		return (0);
+	} else {
+		if (fi->debug) {
+			sprintf(str, "State %s Event %s no routine",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+			fi->printdebug(fi, str);
+		}
+		return (!0);
+	}
+}
+
+void
+FsmChangeState(struct FsmInst *fi, int newstate)
+{
+	char            str[80];
+
+	fi->state = newstate;
+	if (fi->debug) {
+		sprintf(str, "ChangeState %s",
+			fi->fsm->strState[newstate]);
+		fi->printdebug(fi, str);
+	}
+}
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmExpireTimer %lx", (long)ft);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+	FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+	ft->fi = fi;
+	ft->tl.function = (void *) FsmExpireTimer;
+	ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmInitTimer %lx", (long)ft);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+	init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmDelTimer %lx %d", (long)ft, where);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+	del_timer(&ft->tl);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug) {
+		char str[40];
+		sprintf(str, "FsmAddTimer %lx %d %d", (long)ft, millisec, where);
+		ft->fi->printdebug(ft->fi, str);
+	}
+#endif
+
+	if (ft->tl.next || ft->tl.prev) {
+		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+		ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
+		return -1;
+	}
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+	return 0;
+}
+
+int
+FsmTimerRunning(struct FsmTimer *ft)
+{
+	return (ft->tl.next != NULL);
+}
+
+void
+jiftime(char *s, long mark)
+{
+	s += 8;
+
+	*s-- = '\0';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = '.';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 6 + '0';
+	mark /= 6;
+	*s-- = ':';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/hisax.h linux/drivers/isdn/hisax/hisax.h
--- v2.1.26/linux/drivers/isdn/hisax/hisax.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/hisax.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,612 @@
+/* $Id: hisax.h,v 1.11 1997/02/11 01:36:02 keil Exp $
+
+ *   Basic declarations, defines and prototypes
+ *
+ * $Log: hisax.h,v $
+ * Revision 1.11  1997/02/11 01:36:02  keil
+ * New Param structure
+ *
+ * Revision 1.10  1997/02/09 00:23:52  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.9  1997/01/27 23:18:44  keil
+ * prototype for releasestack_isdnl3
+ *
+ * Revision 1.8  1997/01/27 16:02:37  keil
+ * new cards, callc timers, HZDELAY macro, HiSax_getrev prototype
+ *
+ * Revision 1.7  1997/01/21 22:22:14  keil
+ * changes for 2.0; Elsa Quickstep support
+ *
+ * Revision 1.6  1997/01/04 13:48:28  keil
+ * primitiv for MDL_REMOVE added
+ *
+ * Revision 1.5  1996/12/08 19:49:19  keil
+ * Monitor channel support
+ *
+ * Revision 1.4  1996/11/18 15:35:39  keil
+ * some changes for ELSA cards
+ *
+ * Revision 1.3  1996/11/05 19:37:23  keil
+ * using config.h
+ *
+ * Revision 1.2  1996/10/27 22:21:52  keil
+ * CallFlags for broadcast messages
+ *
+ * Revision 1.1  1996/10/13 20:03:46  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+
+#define PH_ACTIVATE   1
+#define PH_DATA       2
+#define PH_DEACTIVATE 3
+
+#define MDL_ASSIGN       4
+#define DL_UNIT_DATA     5
+#define SC_STARTUP       6
+#define CC_ESTABLISH     7
+#define DL_ESTABLISH     8
+#define DL_DATA          9
+#define CC_S_STATUS_ENQ  10
+
+#define CC_CONNECT       15
+#define CC_CONNECT_ACKNOWLEDGE 16
+#define CO_EOF                 17
+#define SC_DISCONNECT          18
+#define CO_DTMF                19
+#define DL_RELEASE             20
+#define DL_FLUSH               21
+
+#define CO_ALARM               22
+#define CC_REJECT              23
+
+#define CC_SETUP_REQ           24
+#define CC_SETUP_CNF           25
+#define CC_SETUP_IND           26
+#define CC_SETUP_RSP           27
+#define CC_SETUP_COMPLETE_IND  28
+
+#define CC_DISCONNECT_REQ      29
+#define CC_DISCONNECT_IND      30
+
+#define CC_RELEASE_CNF         31
+#define CC_RELEASE_IND         32
+#define CC_RELEASE_REQ         33
+
+#define CC_REJECT_REQ          34
+
+#define CC_PROCEEDING_IND      35
+
+#define CC_DLRL                36
+#define CC_DLEST               37
+
+#define CC_ALERTING_REQ        38
+#define CC_ALERTING_IND        39
+
+#define DL_STOP                40
+#define DL_START               41
+
+#define MDL_NOTEIPROC          46
+
+#define LC_ESTABLISH           47
+#define LC_RELEASE             48
+
+#define PH_REQUEST_PULL        49
+#define PH_PULL_ACK            50
+#define	PH_DATA_PULLED	       51
+#define CC_INFO_CHARGE         52
+
+#define CC_MORE_INFO           53
+#define CC_IGNORE              54
+
+#define MDL_REMOVE             56
+#define MDL_VERIFY             57
+
+#define CC_T303                60
+#define CC_T304                61
+#define CC_T305                62
+#define CC_T308_1              64
+#define CC_T308_2              65
+#define CC_T310                66
+#define CC_T313                67
+#define CC_T318                68
+#define CC_T319                69
+
+#define CC_NOSETUP_RSP_ERR     70
+#define CC_SETUP_ERR           71
+#define CC_CONNECT_ERR         72
+#define CC_RELEASE_ERR         73
+
+/*
+ * Message-Types
+ */
+
+#define MT_ALERTING            0x01
+#define MT_CALL_PROCEEDING     0x02
+#define MT_CONNECT             0x07
+#define MT_CONNECT_ACKNOWLEDGE 0x0f
+#define MT_PROGRESS            0x03
+#define MT_SETUP               0x05
+#define MT_SETUP_ACKNOWLEDGE   0x0d
+#define MT_RESUME              0x26
+#define MT_RESUME_ACKNOWLEDGE  0x2e
+#define MT_RESUME_REJECT       0x22
+#define MT_SUSPEND             0x25
+#define MT_SUSPEND_ACKNOWLEDGE 0x2d
+#define MT_SUSPEND_REJECT      0x21
+#define MT_USER_INFORMATION    0x20
+#define MT_DISCONNECT          0x45
+#define MT_RELEASE             0x4d
+#define MT_RELEASE_COMPLETE    0x5a
+#define MT_RESTART             0x46
+#define MT_RESTART_ACKNOWLEDGE 0x4e
+#define MT_SEGMENT             0x60
+#define MT_CONGESTION_CONTROL  0x79
+#define MT_INFORMATION         0x7b
+#define MT_FACILITY            0x62
+#define MT_NOTIFY              0x6e
+#define MT_STATUS              0x7d
+#define MT_STATUS_ENQUIRY      0x75
+
+#define IE_CAUSE               0x08
+
+struct HscxIoctlArg {
+	int channel;
+	int mode;
+	int transbufsize;
+};
+
+#ifdef __KERNEL__
+
+#undef DEBUG_MAGIC
+
+#define HSCX_SBUF_ORDER     1
+#define HSCX_SBUF_BPPS      2
+#define HSCX_SBUF_MAXPAGES  3
+
+#define HSCX_RBUF_ORDER     1
+#define HSCX_RBUF_BPPS      2
+#define HSCX_RBUF_MAXPAGES  3
+
+#define HSCX_SMALLBUF_ORDER     0
+#define HSCX_SMALLBUF_BPPS      40
+#define HSCX_SMALLBUF_MAXPAGES  1
+
+#define ISAC_SBUF_ORDER     0
+#define ISAC_SBUF_BPPS      16
+#define ISAC_SBUF_MAXPAGES  1
+
+#define ISAC_RBUF_ORDER     0
+#define ISAC_RBUF_BPPS      16
+#define ISAC_RBUF_MAXPAGES  1
+
+#define ISAC_SMALLBUF_ORDER     0
+#define ISAC_SMALLBUF_BPPS      40
+#define ISAC_SMALLBUF_MAXPAGES  1
+
+#define byte unsigned char
+
+#define MAX_WINDOW 8
+
+byte *Smalloc(int size, int pr, char *why);
+void Sfree(byte * ptr);
+
+/*
+ * Statemachine
+ */
+struct Fsm {
+	int *jumpmatrix;
+	int state_count, event_count;
+	char **strEvent, **strState;
+};
+
+struct FsmInst {
+	struct Fsm *fsm;
+	int state;
+	int debug;
+	void *userdata;
+	int userint;
+	void (*printdebug) (struct FsmInst *, char *);
+};
+
+struct FsmNode {
+	int state, event;
+	void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+	struct FsmInst *fi;
+	struct timer_list tl;
+	int event;
+	void *arg;
+};
+
+struct L3Timer {
+	struct PStack *st;
+	struct timer_list tl;
+	int event;
+};
+
+struct BufHeader {
+#ifdef DEBUG_MAGIC
+	int magic;
+#endif
+	struct BufHeader *next;
+	struct BufPool *bp;
+	int datasize;
+	byte primitive, where;
+	void *heldby;
+};
+
+struct Pages {
+	struct Pages *next;
+};
+
+struct BufPool {
+#ifdef DEBUG_MAGIC
+	int magic;
+#endif
+	struct BufHeader *freelist;
+	struct Pages *pageslist;
+	int pageorder;
+	int pagescount;
+	int bpps;
+	int bufsize;
+	int maxpages;
+};
+
+struct BufQueue {
+#ifdef DEBUG_MAGIC
+	int magic;
+#endif
+	struct BufHeader *head, *tail;
+};
+
+struct Layer1 {
+	void *hardware;
+	int hscx;
+	struct BufPool *sbufpool, *rbufpool, *smallpool;
+	struct PStack **stlistp;
+	int act_state;
+	void (*l1l2) (struct PStack *, int, struct BufHeader *);
+	void (*l1man) (struct PStack *, int, void *);
+	int hscxmode, hscxchannel, requestpull;
+};
+
+struct Layer2 {
+	int sap, tei, ces;
+	int extended, laptype;
+	int uihsize, ihsize;
+	int vs, va, vr;
+	struct BufQueue i_queue;
+	int window, orig;
+	int rejexp;
+	int debug;
+	struct BufHeader *windowar[MAX_WINDOW];
+	int sow;
+	struct FsmInst l2m;
+	void (*l2l1) (struct PStack *, int, struct BufHeader *);
+	void (*l2l1discardq) (struct PStack *, int, void *, int);
+	void (*l2man) (struct PStack *, int, void *);
+	void (*l2l3) (struct PStack *, int, void *);
+	void (*l2tei) (struct PStack *, int, void *);
+	struct FsmTimer t200_timer, t203_timer;
+	int t200, n200, t203;
+	int rc, t200_running;
+	char debug_id[32];
+};
+
+struct Layer3 {
+	void (*l3l4) (struct PStack *, int, struct BufHeader *);
+	void (*l3l2) (struct PStack *, int, void *);
+	int state, callref;
+	struct L3Timer timer;
+	int t303, t304, t305, t308, t310, t313, t318, t319;
+	int n_t303;
+	int debug;
+	int channr;
+};
+
+struct Layer4 {
+	void (*l4l3) (struct PStack *, int, void *);
+	void *userdata;
+	void (*l1writewakeup) (struct PStack *);
+	void (*l2writewakeup) (struct PStack *);
+};
+
+struct Management {
+	void (*manl1) (struct PStack *, int, void *);
+	void (*manl2) (struct PStack *, int, void *);
+	void (*teil2) (struct PStack *, int, void *);
+};
+
+struct Param {
+	int cause;
+	int loc;
+	int bchannel;
+	int callref;		/* Callreferenz Number */
+	setup_parm setup;       /* from isdnif.h numbers and Serviceindicator */
+	int chargeinfo;		/* Charge Info - only for 1tr6 in
+				 * the moment
+				 */
+	int spv;		/* SPV Flag */
+};
+
+struct PStack {
+	struct PStack *next;
+	struct Layer1 l1;
+	struct Layer2 l2;
+	struct Layer3 l3;
+	struct Layer4 l4;
+	struct Management ma;
+	struct Param *pa;
+	int protocol;		/* EDSS1 or 1TR6 */
+};
+
+struct HscxState {
+	int inuse, init, active;
+	struct BufPool sbufpool, rbufpool, smallpool;
+	struct IsdnCardState *sp;
+	int hscx, mode;
+	int transbufsize, receive;
+	struct BufHeader *rcvibh, *xmtibh;
+	int rcvptr, sendptr;
+	struct PStack *st;
+	struct tq_struct tqueue;
+	int event;
+	struct BufQueue rq, sq;
+	int releasebuf;
+#ifdef DEBUG_MAGIC
+	int magic;		/* 301270 */
+#endif
+};
+
+struct LcFsm {
+	struct FsmInst lcfi;
+	int type;
+	struct Channel *ch;
+	void (*lccall) (struct LcFsm *, int, void *);
+	struct PStack *st;
+	int l2_establish;
+	int l2_start;
+	struct FsmTimer act_timer;
+	char debug_id[32];
+};
+
+struct Channel {
+	struct PStack ds, is;
+	struct IsdnCardState *sp;
+	int hscx;
+	int chan;
+	int incoming;
+	struct FsmInst fi;
+	struct LcFsm lc_d, lc_b;
+	struct Param para;
+	struct FsmTimer drel_timer, dial_timer;
+	int debug;
+#ifdef DEBUG_MAGIC
+	int magic;		/* 301272 */
+#endif
+	int l2_protocol, l2_active_protocol;
+	int l2_primitive, l2_headersize;
+	int data_open;
+	int outcallref;
+	int impair;
+	int Flags;		/* for remembering action done in l4 */
+	int leased;
+};
+
+struct IsdnCardState {
+#ifdef DEBUG_MAGIC
+	int magic;
+#endif
+	unsigned char typ;
+	unsigned char subtyp;
+	int protocol;
+	unsigned int irq;
+	unsigned int cfg_reg;
+	unsigned int membase;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned int counter;
+	int	     myid;
+	isdn_if	     iif;
+	byte	*status_buf;
+	byte    *status_read;
+	byte    *status_write;
+	byte    *status_end;
+	struct BufHeader *mon_rx, *mon_tx;
+	int mon_rxp, mon_txp, mon_flg;
+	void (*ph_command) (struct IsdnCardState *, unsigned int);
+	void (*modehscx) (struct HscxState *, int, int);
+	void (*hscx_fill_fifo) (struct HscxState *);
+	void (*isac_fill_fifo) (struct IsdnCardState *);
+	struct BufPool sbufpool, rbufpool, smallpool;
+	struct Channel channel[2];
+	struct PStack *stlist;
+	struct BufHeader *xmtibh, *rcvibh;
+	int rcvptr, sendptr;
+	int event;
+	struct tq_struct tqueue;
+	int ph_active;
+	struct BufQueue rq, sq;
+	int cardnr;
+	int ph_state;
+	struct PStack *teistack;
+	struct HscxState hs[2];
+	int dlogflag;
+	char *dlogspace;
+	int debug;
+	int releasebuf;
+	unsigned int CallFlags;
+};
+
+#define  MON0_RX	1
+#define  MON1_RX	2
+#define  MON0_TX	4
+#define  MON1_TX	8
+
+#define  ISDN_CTYPE_16_0	1
+#define  ISDN_CTYPE_8_0		2
+#define  ISDN_CTYPE_16_3	3
+#define  ISDN_CTYPE_PNP		4
+#define  ISDN_CTYPE_A1		5
+#define  ISDN_CTYPE_ELSA	6
+#define  ISDN_CTYPE_ELSA_QS1000	7
+#define  ISDN_CTYPE_TELESPCMCIA	8
+#define  ISDN_CTYPE_IX1MICROR2	9
+
+#define  ISDN_CTYPE_COUNT	9
+
+#ifdef	CONFIG_HISAX_16_0
+#define  CARD_TELES0 (1<< ISDN_CTYPE_16_0) | (1<< ISDN_CTYPE_8_0)
+#else
+#define  CARD_TELES0  0
+#endif
+
+#ifdef	CONFIG_HISAX_16_3
+#define  CARD_TELES3 (1<< ISDN_CTYPE_16_3) | (1<< ISDN_CTYPE_PNP) | \
+		     (1<< ISDN_CTYPE_TELESPCMCIA)
+#else
+#define  CARD_TELES3  0
+#endif
+
+#ifdef	CONFIG_HISAX_AVM_A1
+#define  CARD_AVM_A1 (1<< ISDN_CTYPE_A1)
+#else
+#define  CARD_AVM_A1  0
+#endif
+
+#ifdef	CONFIG_HISAX_ELSA_PCC
+#define  CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_QS1000)
+#else
+#define  CARD_ELSA  0
+#endif
+
+#ifdef	CONFIG_HISAX_IX1MICROR2
+#define	CARD_IX1MICROR2 (1 << ISDN_CTYPE_IX1MICROR2)
+#else
+#define CARD_IX1MICROR2 0
+#endif
+
+#define  SUPORTED_CARDS  (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \
+			 | CARD_IX1MICROR2)
+
+struct IsdnCard {
+	int typ;
+	int protocol;		/* EDSS1 or 1TR6 */
+	unsigned int para[3];
+	struct IsdnCardState *sp;
+};
+
+#define DATAPTR(x) ((byte *)x+sizeof(struct BufHeader))
+
+#define LAPD 0
+#define LAPB 1
+
+void BufPoolInit(struct BufPool *bp, int order, int bpps,
+		 int maxpages);
+int BufPoolAdd(struct BufPool *bp, int priority);
+void BufPoolFree(struct BufPool *bp);
+int BufPoolGet(struct BufHeader **bh, struct BufPool *bp,
+	       int priority, void *heldby, int where);
+void BufPoolRelease(struct BufHeader *bh);
+void BufQueueLink(struct BufQueue *bq, struct BufHeader *bh);
+int BufQueueUnlink(struct BufHeader **bh, struct BufQueue *bq);
+void BufQueueInit(struct BufQueue *bq);
+void BufQueueRelease(struct BufQueue *bq);
+void BufQueueDiscard(struct BufQueue *q, int pr, void *heldby,
+		     int releasetoo);
+int BufQueueLength(struct BufQueue *bq);
+void BufQueueLinkFront(struct BufQueue *bq, struct BufHeader *bh);
+
+void l2down(struct PStack *st, byte pr, struct BufHeader *ibh);
+void l2up(struct PStack *st, byte pr, struct BufHeader *ibh);
+void acceptph(struct PStack *st, struct BufHeader *ibh);
+void setstack_isdnl2(struct PStack *st, char *debug_id);
+int HiSax_inithardware(void);
+void HiSax_closehardware(void);
+
+void setstack_HiSax(struct PStack *st, struct IsdnCardState *sp);
+unsigned int randomces(void);
+void setstack_isdnl3(struct PStack *st, struct Channel *chanp );
+void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void releasestack_isdnl2(struct PStack *st);
+void releasestack_isdnl3(struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
+void newcallref(struct PStack *st);
+
+int setstack_hscx(struct PStack *st, struct HscxState *hs);
+byte *findie(byte * p, int size, byte ie, int wanted_set);
+int getcallref(byte * p);
+
+void FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec,
+		int event, void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+int FsmTimerRunning(struct FsmTimer *ft);
+void jiftime(char *s, long mark);
+
+int HiSax_command(isdn_ctrl * ic);
+int HiSax_writebuf(int id, int chan, const u_char * buf, int count, int user);
+void HiSax_putstatus(struct IsdnCardState *csta, char *buf);
+void HiSax_reportcard(int cardnr);
+int ListLength(struct BufHeader *ibh);
+int QuickHex(char *txt, byte * p, int cnt);
+void LogFrame(struct IsdnCardState *sp, byte * p, int size);
+void dlogframe(struct IsdnCardState *sp, byte * p, int size, char *comment);
+void iecpy(byte * dest, byte * iestart, int ieoffset);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+void close_hscxstate(struct HscxState *);
+void setstack_tei(struct PStack *st);
+
+
+
+
+#define PART_SIZE(order,bpps) (( (PAGE_SIZE<<order) -\
+  sizeof(void *))/bpps)
+#define BUFFER_SIZE(order,bpps) (PART_SIZE(order,bpps)-\
+  sizeof(struct BufHeader))
+
+#endif				/* __KERNEL__ */
+
+#define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
+
+int ll_run(struct IsdnCardState *csta);
+void ll_stop(struct IsdnCardState *csta);
+void CallcNew(void);
+void CallcFree(void);
+int CallcNewChan(struct IsdnCardState *csta);
+void CallcFreeChan(struct IsdnCardState *csta);
+void Isdnl2New(void);
+void Isdnl2Free(void);
+void init_tei(struct IsdnCardState *sp, int protocol);
+void release_tei(struct IsdnCardState *sp);
+char *HiSax_getrev(const char *revision);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/isdnl1.c linux/drivers/isdn/hisax/isdnl1.c
--- v2.1.26/linux/drivers/isdn/hisax/isdnl1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl1.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,1340 @@
+/* $Id: isdnl1.c,v 1.8 1997/02/09 00:24:31 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)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ *
+ * $Log: isdnl1.c,v $
+ * Revision 1.8  1997/02/09 00:24:31  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.7  1997/01/27 15:56:03  keil
+ * PCMCIA Teles card and ITK ix1 micro added
+ *
+ * Revision 1.6  1997/01/21 22:20:00  keil
+ * changes for D-channel log; Elsa Quickstep support
+ *
+ * Revision 1.5  1997/01/10 12:51:19  keil
+ * cleanup; set newversion
+ *
+ * Revision 1.4  1996/12/08 19:44:53  keil
+ * L2FRAME_DEBUG and other changes from Pekka Sarnila
+ *
+ * Revision 1.3  1996/11/18 15:34:47  keil
+ * fix HSCX version code
+ *
+ * Revision 1.2  1996/10/27 22:16:54  keil
+ * ISAC/HSCX version lookup
+ *
+ * Revision 1.1  1996/10/13 20:04:53  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+
+const char *l1_revision = "$Revision: 1.8 $";
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl1.h"
+
+#if CARD_TELES0
+#include "teles0.h"
+#endif
+
+#if CARD_TELES3
+#include "teles3.h"
+#endif
+
+#if CARD_AVM_A1
+#include "avm_a1.h"
+#endif
+
+#if CARD_ELSA
+#include "elsa.h"
+#endif
+
+#if CARD_IX1MICROR2
+#include "ix1_micro.h"
+#endif
+
+#define 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 PnP", "AVM A1", "Elsa ML", "Elsa Quickstep",
+ "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 void tei_handler(struct PStack *st, byte pr,
+			struct BufHeader *ibh);
+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(byte * buf, int len, int user, int id, int channel)
+{
+	int count;
+	byte *p;
+	struct IsdnCardState *csta = hisax_findcard(id);
+
+	if (csta) {
+		for (p = buf, count = 0; count < len; p++, count++) {
+			if (user)
+				put_user(*csta->status_read++, p);
+			else
+				*p++ = *csta->status_read++;
+			if (csta->status_read > csta->status_end)
+				csta->status_read = csta->status_buf;
+		}
+		return count;
+	} else {
+		printk(KERN_ERR
+		 "HiSax: if_readstatus called with invalid driverId!\n");
+		return -ENODEV;
+	}
+}
+
+void
+HiSax_putstatus(struct IsdnCardState *csta, char *buf)
+{
+	long flags;
+	int len, count, i;
+	byte *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)
+		Sfree(csta->status_buf);
+	csta->status_read = NULL;
+	csta->status_write = NULL;
+	csta->status_end = NULL;
+	Sfree(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(byte v)
+{
+	return (HSCXVer[v & 0xf]);
+}
+
+void
+hscx_sched_event(struct HscxState *hsp, int event)
+{
+	hsp->event |= 1 << event;
+	queue_task_irq_off(&hsp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+char *
+ISACVersion(byte v)
+{
+	return (ISACVer[(v >> 5) & 3]);
+}
+
+void
+isac_sched_event(struct IsdnCardState *sp, int event)
+{
+	sp->event |= 1 << event;
+	queue_task_irq_off(&sp->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+}
+
+int
+act_wanted(struct IsdnCardState *sp)
+{
+	struct PStack *st;
+
+	st = sp->stlist;
+	while (st)
+		if (st->l1.act_state)
+			return (!0);
+		else
+			st = st->next;
+	return (0);
+}
+
+void
+isac_new_ph(struct IsdnCardState *sp)
+{
+	int enq;
+
+	enq = act_wanted(sp);
+
+	switch (sp->ph_state) {
+		case (6):
+			sp->ph_active = 0;
+			sp->ph_command(sp, 15);
+			break;
+		case (15):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 0);
+			break;
+		case (0):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 0);
+#if 0
+			else
+				sp->ph_command(sp, 15);
+#endif
+			break;
+		case (7):
+			sp->ph_active = 0;
+			if (enq)
+				sp->ph_command(sp, 9);
+			break;
+		case (12):
+			sp->ph_command(sp, 8);
+			sp->ph_active = 5;
+			isac_sched_event(sp, ISAC_PHCHANGE);
+			if (!sp->xmtibh)
+				if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
+					sp->sendptr = 0;
+			if (sp->xmtibh)
+				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->xmtibh)
+				if (!BufQueueUnlink(&sp->xmtibh, &sp->sq))
+					sp->sendptr = 0;
+			if (sp->xmtibh)
+				sp->isac_fill_fifo(sp);
+			break;
+		case (4):
+		case (8):
+			sp->ph_active = 0;
+			break;
+		default:
+			sp->ph_active = 0;
+			break;
+	}
+}
+
+static void
+restart_ph(struct IsdnCardState *sp)
+{
+	if (!sp->ph_active) {
+		if ((sp->ph_state == 6) || (sp->ph_state == 0)) {
+			sp->ph_command(sp, 0);
+			sp->ph_active = 2;
+		} else {
+			sp->ph_command(sp, 1);
+			sp->ph_active = 1;
+		}
+	} else if (sp->ph_active == 2) {
+		sp->ph_command(sp, 1);
+		sp->ph_active = 1;
+	}
+}
+
+
+static void
+act_ivated(struct IsdnCardState *sp)
+{
+	struct PStack *st;
+
+	st = sp->stlist;
+	while (st) {
+		if (st->l1.act_state == 1) {
+			st->l1.act_state = 2;
+			st->l1.l1man(st, PH_ACTIVATE, NULL);
+		}
+		st = st->next;
+	}
+}
+
+static void
+process_new_ph(struct IsdnCardState *sp)
+{
+	if (sp->ph_active == 5)
+		act_ivated(sp);
+}
+
+static void
+process_xmt(struct IsdnCardState *sp)
+{
+	struct PStack *stptr;
+
+	if (sp->xmtibh)
+		return;
+
+	stptr = sp->stlist;
+	while (stptr != NULL)
+		if (stptr->l1.requestpull) {
+			stptr->l1.requestpull = 0;
+			stptr->l1.l1l2(stptr, PH_PULL_ACK, NULL);
+			break;
+		} else
+			stptr = stptr->next;
+}
+
+static void
+process_rcv(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh, *cibh;
+	struct PStack *stptr;
+	byte *ptr;
+	int found, broadc;
+	char tmp[64];
+
+	while (!BufQueueUnlink(&ibh, &sp->rq)) {
+#ifdef L2FRAME_DEBUG		/* psa */
+		if (sp->debug & L1_DEB_LAPD)
+			Logl2Frame(sp, ibh, "PH_DATA", 1);
+#endif
+		stptr = sp->stlist;
+		ptr = DATAPTR(ibh);
+		broadc = (ptr[1] >> 1) == 127;
+
+		if (broadc) {
+			if (!(ptr[0] >> 2)) {	/* sapi 0 */
+				sp->CallFlags = 3;
+				if (sp->dlogflag) {
+					LogFrame(sp, ptr, ibh->datasize);
+					dlogframe(sp, ptr + 3, ibh->datasize - 3,
+						  "Q.931 frame network->user broadcast");
+				}
+			}
+			while (stptr != NULL) {
+				if ((ptr[0] >> 2) == stptr->l2.sap)
+					if (!BufPoolGet(&cibh, &sp->rbufpool, GFP_ATOMIC,
+							(void *) 1, 5)) {
+						memcpy(DATAPTR(cibh), DATAPTR(ibh), ibh->datasize);
+						cibh->datasize = ibh->datasize;
+						stptr->l1.l1l2(stptr, PH_DATA, cibh);
+					} else
+						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
+				stptr = stptr->next;
+			}
+			BufPoolRelease(ibh);
+		} else {
+			found = 0;
+			while (stptr != NULL)
+				if (((ptr[0] >> 2) == stptr->l2.sap) &&
+				    ((ptr[1] >> 1) == stptr->l2.tei)) {
+					stptr->l1.l1l2(stptr, PH_DATA, ibh);
+					found = !0;
+					break;
+				} else
+					stptr = stptr->next;
+			if (!found) {
+				/* BD 10.10.95
+				 * Print out D-Channel msg not processed
+				 * by isdn4linux
+				 */
+
+				if ((!(ptr[0] >> 2)) && (!(ptr[2] & 0x01))) {
+					sprintf(tmp,
+						"Q.931 frame network->user with tei %d (not for us)",
+						ptr[1] >> 1);
+					LogFrame(sp, ptr, ibh->datasize);
+					dlogframe(sp, ptr + 4, ibh->datasize - 4, tmp);
+				}
+				BufPoolRelease(ibh);
+			}
+		}
+
+	}
+
+}
+
+static void
+isac_bh(struct IsdnCardState *sp)
+{
+	if (!sp)
+		return;
+
+	if (clear_bit(ISAC_PHCHANGE, &sp->event))
+		process_new_ph(sp);
+	if (clear_bit(ISAC_RCVBUFREADY, &sp->event))
+		process_rcv(sp);
+	if (clear_bit(ISAC_XMTBUFREADY, &sp->event))
+		process_xmt(sp);
+}
+
+static void
+l2l1(struct PStack *st, int pr,
+     struct BufHeader *ibh)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	byte *ptr = DATAPTR(ibh);
+	char str[64];
+
+	switch (pr) {
+		case (PH_DATA):
+			if (sp->xmtibh) {
+				BufQueueLink(&sp->sq, ibh);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (sp->debug & L1_DEB_LAPD)
+					Logl2Frame(sp, ibh, "PH_DATA Queued", 0);
+#endif
+			} else {
+				if ((sp->dlogflag) && (!(ptr[2] & 1))) {	/* I-FRAME */
+					LogFrame(sp, ptr, ibh->datasize);
+					sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+					dlogframe(sp, ptr + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+						  str);
+				}
+				sp->xmtibh = ibh;
+				sp->sendptr = 0;
+				sp->releasebuf = !0;
+				sp->isac_fill_fifo(sp);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (sp->debug & L1_DEB_LAPD)
+					Logl2Frame(sp, ibh, "PH_DATA", 0);
+#endif
+			}
+			break;
+		case (PH_DATA_PULLED):
+			if (sp->xmtibh) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, " l2l1 xmtibh exist this shouldn't happen");
+				break;
+			}
+			if ((sp->dlogflag) && (!(ptr[2] & 1))) {	/* I-FRAME */
+				LogFrame(sp, ptr, ibh->datasize);
+				sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
+				dlogframe(sp, ptr + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+					  str);
+			}
+			sp->xmtibh = ibh;
+			sp->sendptr = 0;
+			sp->releasebuf = 0;
+			sp->isac_fill_fifo(sp);
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (sp->debug & L1_DEB_LAPD)
+				Logl2Frame(sp, ibh, "PH_DATA_PULLED", 0);
+#endif
+			break;
+		case (PH_REQUEST_PULL):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (sp->debug & L1_DEB_LAPD)
+				debugl1(sp, "-> PH_REQUEST_PULL");
+#endif
+			if (!sp->xmtibh) {
+				st->l1.requestpull = 0;
+				st->l1.l1l2(st, PH_PULL_ACK, NULL);
+			} else
+				st->l1.requestpull = !0;
+			break;
+	}
+}
+
+
+static void
+hscx_process_xmt(struct HscxState *hsp)
+{
+	struct PStack *st = hsp->st;
+
+	if (hsp->xmtibh)
+		return;
+
+	if (st->l1.requestpull) {
+		st->l1.requestpull = 0;
+		st->l1.l1l2(st, PH_PULL_ACK, NULL);
+	}
+	if (!hsp->active)
+		if ((!hsp->xmtibh) && (!hsp->sq.head))
+			hsp->sp->modehscx(hsp, 0, 0);
+}
+
+static void
+hscx_process_rcv(struct HscxState *hsp)
+{
+	struct BufHeader *ibh;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_process_rcv magic not 301270\n");
+		return;
+	}
+#endif
+	while (!BufQueueUnlink(&ibh, &hsp->rq)) {
+		hsp->st->l1.l1l2(hsp->st, PH_DATA, ibh);
+	}
+}
+
+static void
+hscx_bh(struct HscxState *hsp)
+{
+
+	if (!hsp)
+		return;
+
+	if (clear_bit(HSCX_RCVBUFREADY, &hsp->event))
+		hscx_process_rcv(hsp);
+	if (clear_bit(HSCX_XMTBUFREADY, &hsp->event))
+		hscx_process_xmt(hsp);
+
+}
+
+/*
+ * interrupt stuff ends here
+ */
+
+void
+HiSax_addlist(struct IsdnCardState *sp,
+	      struct PStack *st)
+{
+	st->next = sp->stlist;
+	sp->stlist = st;
+}
+
+void
+HiSax_rmlist(struct IsdnCardState *sp,
+	     struct PStack *st)
+{
+	struct PStack *p;
+
+	if (sp->stlist == st)
+		sp->stlist = st->next;
+	else {
+		p = sp->stlist;
+		while (p)
+			if (p->next == st) {
+				p->next = st->next;
+				return;
+			} else
+				p = p->next;
+	}
+}
+
+static void
+check_ph_act(struct IsdnCardState *sp)
+{
+	struct PStack *st = sp->stlist;
+
+	while (st) {
+		if (st->l1.act_state)
+			return;
+		st = st->next;
+	}
+	if (sp->ph_active == 5)
+		sp->ph_active = 4;
+}
+
+static void
+HiSax_manl1(struct PStack *st, int pr,
+	    void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	long flags;
+	char tmp[32];
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			if (sp->debug) {
+				sprintf(tmp, "PH_ACT ph_active %d", sp->ph_active);
+				debugl1(sp, tmp);
+			}
+			save_flags(flags);
+			cli();
+			if (sp->ph_active & 4) {
+				sp->ph_active = 5;
+				st->l1.act_state = 2;
+				restore_flags(flags);
+				st->l1.l1man(st, PH_ACTIVATE, NULL);
+			} else {
+				st->l1.act_state = 1;
+				if (sp->ph_active == 0)
+					restart_ph(sp);
+				restore_flags(flags);
+			}
+			break;
+		case (PH_DEACTIVATE):
+			st->l1.act_state = 0;
+			if (sp->debug) {
+				sprintf(tmp, "PH_DEACT ph_active %d", sp->ph_active);
+				debugl1(sp, tmp);
+			}
+			check_ph_act(sp);
+			break;
+	}
+}
+
+static void
+HiSax_l2l1discardq(struct PStack *st, int pr,
+		   void *heldby, int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+
+#ifdef DEBUG_MAGIC
+	if (sp->magic != 301271) {
+		printk(KERN_DEBUG "isac_discardq magic not 301271\n");
+		return;
+	}
+#endif
+
+	BufQueueDiscard(&sp->sq, pr, heldby, releasetoo);
+}
+
+void
+setstack_HiSax(struct PStack *st, struct IsdnCardState *sp)
+{
+	st->l1.hardware = sp;
+	st->l1.sbufpool = &(sp->sbufpool);
+	st->l1.rbufpool = &(sp->rbufpool);
+	st->l1.smallpool = &(sp->smallpool);
+	st->protocol = sp->protocol;
+
+	setstack_tei(st);
+
+	st->l1.stlistp = &(sp->stlist);
+	st->l1.act_state = 0;
+	st->l2.l2l1 = l2l1;
+	st->l2.l2l1discardq = HiSax_l2l1discardq;
+	st->ma.manl1 = HiSax_manl1;
+	st->l1.requestpull = 0;
+}
+
+void
+init_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	hsp->sp = sp;
+	hsp->hscx = hscx;
+
+	hsp->tqueue.next = 0;
+	hsp->tqueue.sync = 0;
+	hsp->tqueue.routine = (void *) (void *) hscx_bh;
+	hsp->tqueue.data = hsp;
+
+	hsp->inuse = 0;
+	hsp->init = 0;
+	hsp->active = 0;
+
+#ifdef DEBUG_MAGIC
+	hsp->magic = 301270;
+#endif
+}
+
+int
+get_irq(int cardnr, void *routine)
+{
+	struct IsdnCard *card = cards + cardnr;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	if (request_irq(card->sp->irq, routine,
+			SA_INTERRUPT, "HiSax", NULL)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+		       card->sp->irq);
+		restore_flags(flags);
+		return (0);
+	}
+	irq2dev_map[card->sp->irq] = (void *) card->sp;
+	restore_flags(flags);
+	return (1);
+}
+
+static void
+release_irq(int cardnr)
+{
+	struct IsdnCard *card = cards + cardnr;
+
+	irq2dev_map[card->sp->irq] = NULL;
+	free_irq(card->sp->irq, NULL);
+}
+
+void
+close_hscxstate(struct HscxState *hs)
+{
+	hs->sp->modehscx(hs, 0, 0);
+	hs->inuse = 0;
+
+	if (hs->init) {
+		BufPoolFree(&hs->smallpool);
+		BufPoolFree(&hs->rbufpool);
+		BufPoolFree(&hs->sbufpool);
+	}
+	hs->init = 0;
+}
+
+static void
+closecard(int cardnr)
+{
+	struct IsdnCardState *csta = cards[cardnr].sp;
+
+	BufPoolFree(&csta->smallpool);
+	BufPoolFree(&csta->rbufpool);
+	BufPoolFree(&csta->sbufpool);
+
+	close_hscxstate(csta->hs + 1);
+	close_hscxstate(csta->hs);
+
+	switch (csta->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			release_io_teles0(cards + cardnr);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_TELESPCMCIA:
+			release_io_teles3(cards + cardnr);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			release_io_avm_a1(cards + cardnr);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			release_io_elsa(cards + cardnr);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			release_io_ix1micro(cards + cardnr);
+			break;
+#endif
+		default:
+			break;
+	}
+	ll_unload(csta);
+}
+
+static int
+checkcard(int cardnr, char *id)
+{
+	long flags;
+	int ret = 0;
+	struct IsdnCard *card = cards + cardnr;
+	struct IsdnCardState *sp;
+
+	save_flags(flags);
+	cli();
+	if (!(sp = (struct IsdnCardState *)
+	      Smalloc(sizeof(struct IsdnCardState), GFP_KERNEL,
+		      "struct IsdnCardState"))) {
+		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 = Smalloc(4096, GFP_KERNEL, "dlogspace"))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for dlogspace(card %d)\n",
+		       cardnr + 1);
+		restore_flags(flags);
+		return (0);
+	}
+	if (!(sp->status_buf = Smalloc(HISAX_STATUS_BUFSIZE, GFP_KERNEL, "status_buf"))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for status_buf(card %d)\n",
+		       cardnr + 1);
+		Sfree(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 = BUFFER_SIZE(HSCX_SBUF_ORDER, HSCX_SBUF_BPPS);
+	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
+	    0;
+
+	sp->iif.command = HiSax_command;
+	sp->iif.writebuf = HiSax_writebuf;
+	sp->iif.writecmd = NULL;
+	sp->iif.writebuf_skb = NULL;
+	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" :
+	       "NONE", sp->iif.id, sp->myid);
+	switch (card->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			ret = setup_teles0(card);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_TELESPCMCIA:
+			ret = setup_teles3(card);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = setup_avm_a1(card);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			ret = setup_elsa(card);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = setup_ix1micro(card);
+			break;
+#endif
+		default:
+			printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
+			       card->typ);
+			ll_unload(sp);
+			return (0);
+	}
+	if (!ret) {
+		ll_unload(sp);
+		return (0);
+	}
+	BufPoolInit(&sp->sbufpool, ISAC_SBUF_ORDER, ISAC_SBUF_BPPS,
+		    ISAC_SBUF_MAXPAGES);
+	BufPoolInit(&sp->rbufpool, ISAC_RBUF_ORDER, ISAC_RBUF_BPPS,
+		    ISAC_RBUF_MAXPAGES);
+	BufPoolInit(&sp->smallpool, ISAC_SMALLBUF_ORDER, ISAC_SMALLBUF_BPPS,
+		    ISAC_SMALLBUF_MAXPAGES);
+	sp->rcvibh = NULL;
+	sp->rcvptr = 0;
+	sp->xmtibh = NULL;
+	sp->sendptr = 0;
+	sp->mon_rx = NULL;
+	sp->mon_rxp = 0;
+	sp->mon_tx = NULL;
+	sp->mon_txp = 0;
+	sp->mon_flg = 0;
+	sp->event = 0;
+	sp->tqueue.next = 0;
+	sp->tqueue.sync = 0;
+	sp->tqueue.routine = (void *) (void *) isac_bh;
+	sp->tqueue.data = sp;
+
+	BufQueueInit(&sp->rq);
+	BufQueueInit(&sp->sq);
+
+	sp->stlist = NULL;
+	sp->ph_active = 0;
+	sp->dlogflag = 0;
+	sp->debug = L1_DEB_WARN;
+	sp->releasebuf = 0;
+#ifdef DEBUG_MAGIC
+	sp->magic = 301271;
+#endif
+
+	init_hscxstate(sp, 0);
+	init_hscxstate(sp, 1);
+
+	switch (card->typ) {
+#if CARD_TELES0
+		case ISDN_CTYPE_16_0:
+		case ISDN_CTYPE_8_0:
+			ret = initteles0(sp);
+			break;
+#endif
+#if CARD_TELES3
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_TELESPCMCIA:
+			ret = initteles3(sp);
+			break;
+#endif
+#if CARD_AVM_A1
+		case ISDN_CTYPE_A1:
+			ret = initavm_a1(sp);
+			break;
+#endif
+#if CARD_ELSA
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_ELSA_QS1000:
+			ret = initelsa(sp);
+			break;
+#endif
+#if CARD_IX1MICROR2
+		case ISDN_CTYPE_IX1MICROR2:
+			ret = initix1micro(sp);
+			break;
+#endif
+		default:
+			ret = 0;
+			break;
+	}
+	if (!ret) {
+		closecard(cardnr);
+		return (0);
+	}
+	init_tei(sp, sp->protocol);
+	CallcNewChan(sp);
+	ll_run(sp);
+	return (1);
+}
+
+void
+HiSax_shiftcards(int idx)
+{
+	int i;
+
+	for (i = idx; i < 15; i++)
+		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+int
+HiSax_inithardware(void)
+{
+	int foundcards = 0;
+	int i = 0;
+	int t = ',';
+	int flg = 0;
+	char *id;
+	char *next_id = HiSax_id;
+	char ids[20];
+
+	if (strchr(HiSax_id, ','))
+		t = ',';
+	else if (strchr(HiSax_id, '%'))
+		t = '%';
+
+	while (i < nrcards) {
+		if (cards[i].typ < 1)
+			break;
+		id = next_id;
+		if ((next_id = strchr(id, t))) {
+			*next_id++ = 0;
+			strcpy(ids, id);
+			flg = i + 1;
+		} else {
+			next_id = id;
+			if (flg >= i)
+				strcpy(ids, id);
+			else
+				sprintf(ids, "%s%d", id, i);
+		}
+		if (checkcard(i, ids)) {
+			foundcards++;
+			i++;
+		} else {
+			printk(KERN_WARNING "HiSax: Card %s not installed !\n",
+			       CardType[cards[i].typ]);
+			if (cards[i].sp)
+				Sfree((void *) cards[i].sp);
+			cards[i].sp = NULL;
+			HiSax_shiftcards(i);
+		}
+	}
+	return foundcards;
+}
+
+void
+HiSax_closehardware(void)
+{
+	int i;
+	long flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].sp) {
+			ll_stop(cards[i].sp);
+			CallcFreeChan(cards[i].sp);
+			release_tei(cards[i].sp);
+			release_irq(i);
+			closecard(i);
+			Sfree((void *) cards[i].sp);
+			cards[i].sp = NULL;
+		}
+	Isdnl2Free();
+	CallcFree();
+	restore_flags(flags);
+}
+
+static void
+hscx_l2l1(struct PStack *st, int pr,
+	  struct BufHeader *ibh)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+	long flags;
+
+	switch (pr) {
+		case (PH_DATA):
+			save_flags(flags);
+			cli();
+			if (hsp->xmtibh) {
+				BufQueueLink(&hsp->sq, ibh);
+				restore_flags(flags);
+			} else {
+				restore_flags(flags);
+				hsp->xmtibh = ibh;
+				hsp->sendptr = 0;
+				hsp->releasebuf = !0;
+				sp->hscx_fill_fifo(hsp);
+			}
+			break;
+		case (PH_DATA_PULLED):
+			if (hsp->xmtibh) {
+				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
+				break;
+			}
+			hsp->xmtibh = ibh;
+			hsp->sendptr = 0;
+			hsp->releasebuf = 0;
+			sp->hscx_fill_fifo(hsp);
+			break;
+		case (PH_REQUEST_PULL):
+			if (!hsp->xmtibh) {
+				st->l1.requestpull = 0;
+				st->l1.l1l2(st, PH_PULL_ACK, NULL);
+			} else
+				st->l1.requestpull = !0;
+			break;
+	}
+
+}
+extern struct IsdnBuffers *tracebuf;
+
+static void
+hscx_l2l1discardq(struct PStack *st, int pr, void *heldby,
+		  int releasetoo)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *)
+	st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+#ifdef DEBUG_MAGIC
+	if (hsp->magic != 301270) {
+		printk(KERN_DEBUG "hscx_discardq magic not 301270\n");
+		return;
+	}
+#endif
+
+	BufQueueDiscard(&hsp->sq, pr, heldby, releasetoo);
+}
+
+static int
+open_hscxstate(struct IsdnCardState *sp,
+	       int hscx)
+{
+	struct HscxState *hsp = sp->hs + hscx;
+
+	if (!hsp->init) {
+		BufPoolInit(&hsp->sbufpool, HSCX_SBUF_ORDER, HSCX_SBUF_BPPS,
+			    HSCX_SBUF_MAXPAGES);
+		BufPoolInit(&hsp->rbufpool, HSCX_RBUF_ORDER, HSCX_RBUF_BPPS,
+			    HSCX_RBUF_MAXPAGES);
+		BufPoolInit(&hsp->smallpool, HSCX_SMALLBUF_ORDER, HSCX_SMALLBUF_BPPS,
+			    HSCX_SMALLBUF_MAXPAGES);
+	}
+	hsp->init = !0;
+
+	BufQueueInit(&hsp->rq);
+	BufQueueInit(&hsp->sq);
+
+	hsp->releasebuf = 0;
+	hsp->rcvibh = NULL;
+	hsp->xmtibh = NULL;
+	hsp->rcvptr = 0;
+	hsp->sendptr = 0;
+	hsp->event = 0;
+	return (0);
+}
+
+static void
+hscx_manl1(struct PStack *st, int pr,
+	   void *arg)
+{
+	struct IsdnCardState *sp = (struct IsdnCardState *) st->l1.hardware;
+	struct HscxState *hsp = sp->hs + st->l1.hscx;
+
+	switch (pr) {
+		case (PH_ACTIVATE):
+			hsp->active = !0;
+			sp->modehscx(hsp, st->l1.hscxmode, st->l1.hscxchannel);
+			st->l1.l1man(st, PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE):
+			if (!hsp->xmtibh)
+				sp->modehscx(hsp, 0, 0);
+
+			hsp->active = 0;
+			break;
+	}
+}
+
+int
+setstack_hscx(struct PStack *st, struct HscxState *hs)
+{
+	if (open_hscxstate(st->l1.hardware, hs->hscx))
+		return (-1);
+
+	st->l1.hscx = hs->hscx;
+	st->l2.l2l1 = hscx_l2l1;
+	st->ma.manl1 = hscx_manl1;
+	st->l2.l2l1discardq = hscx_l2l1discardq;
+
+	st->l1.sbufpool = &hs->sbufpool;
+	st->l1.rbufpool = &hs->rbufpool;
+	st->l1.smallpool = &hs->smallpool;
+	st->l1.act_state = 0;
+	st->l1.requestpull = 0;
+
+	hs->st = st;
+	return (0);
+}
+
+void
+HiSax_reportcard(int cardnr)
+{
+	struct IsdnCardState *sp = cards[cardnr].sp;
+
+	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[sp->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", sp->debug);
+	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+	       (ulong) & HiSax_reportcard);
+}
+
+#ifdef L2FRAME_DEBUG		/* psa */
+
+char *
+l2cmd(byte cmd)
+{
+	switch (cmd & ~0x10) {
+		case 1:
+			return "RR";
+		case 5:
+			return "RNR";
+		case 9:
+			return "REJ";
+		case 0x6f:
+			return "SABME";
+		case 0x0f:
+			return "DM";
+		case 3:
+			return "UI";
+		case 0x43:
+			return "DISC";
+		case 0x63:
+			return "UA";
+		case 0x87:
+			return "FRMR";
+		case 0xaf:
+			return "XID";
+		default:
+			if (!(cmd & 1))
+				return "I";
+			else
+				return "invalid command";
+	}
+}
+
+static char tmp[20];
+
+char *
+l2frames(byte * ptr)
+{
+	switch (ptr[2] & ~0x10) {
+		case 1:
+		case 5:
+		case 9:
+			sprintf(tmp, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+			break;
+		case 0x6f:
+		case 0x0f:
+		case 3:
+		case 0x43:
+		case 0x63:
+		case 0x87:
+		case 0xaf:
+			sprintf(tmp, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+			break;
+		default:
+			if (!(ptr[2] & 1)) {
+				sprintf(tmp, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+				break;
+			} else
+				return "invalid command";
+	}
+
+
+	return tmp;
+}
+
+void
+Logl2Frame(struct IsdnCardState *sp, struct BufHeader *ibh, char *buf, int dir)
+{
+	char tmp[132];
+	byte *ptr;
+
+	ptr = DATAPTR(ibh);
+
+	if (ptr[0] & 1 || !(ptr[1] & 1))
+		debugl1(sp, "Addres not LAPD");
+	else {
+		sprintf(tmp, "%s %s: %s%c (sapi %d, tei %d)",
+			(dir ? "<-" : "->"), buf, l2frames(ptr),
+			((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
+		debugl1(sp, tmp);
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h
--- v2.1.26/linux/drivers/isdn/hisax/isdnl1.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl1.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,45 @@
+/* $Id: isdnl1.h,v 1.3 1996/12/08 19:41:55 keil Exp $
+ *
+ * $Log: isdnl1.h,v $
+ * Revision 1.3  1996/12/08 19:41:55  keil
+ * L2FRAME_DEBUG
+ *
+ * Revision 1.2  1996/10/27 22:26:27  keil
+ * ISAC/HSCX version functions
+ *
+ * Revision 1.1  1996/10/13 20:03:47  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+
+
+#define L2FRAME_DEBUG
+
+/* DEBUG Level */
+
+#define	L1_DEB_WARN		0x01
+#define	L1_DEB_INTSTAT		0x02
+#define	L1_DEB_ISAC		0x04
+#define	L1_DEB_ISAC_FIFO	0x08
+#define	L1_DEB_HSCX		0x10
+#define	L1_DEB_HSCX_FIFO	0x20
+#define	L1_DEB_LAPD	        0x40
+
+
+#define ISAC_RCVBUFREADY 0
+#define ISAC_XMTBUFREADY 1
+#define ISAC_PHCHANGE    2
+
+#define HSCX_RCVBUFREADY 0
+#define HSCX_XMTBUFREADY 1
+
+extern void debugl1(struct IsdnCardState *sp, char *msg);
+extern char *HscxVersion(byte v);
+extern char *ISACVersion(byte 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);
+extern void Logl2Frame(struct IsdnCardState *sp, struct BufHeader *ibh, char *buf, int dir);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/isdnl2.c linux/drivers/isdn/hisax/isdnl2.c
--- v2.1.26/linux/drivers/isdn/hisax/isdnl2.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl2.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,1465 @@
+/* $Id: isdnl2.c,v 1.7 1997/02/09 00:25:44 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: isdnl2.c,v $
+ * Revision 1.7  1997/02/09 00:25:44  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.6  1997/01/21 22:23:42  keil
+ * D-channel log changed
+ *
+ * Revision 1.5  1997/01/04 13:47:06  keil
+ * handling of MDL_REMOVE added (Thanks to Sim Yskes)
+ *
+ * Revision 1.4  1996/12/08 19:51:51  keil
+ * many fixes from Pekka Sarnila
+ *
+ * Revision 1.3  1996/11/05 19:39:12  keil
+ * X.75 bugfixes Thank to Martin Maurer
+ *
+ * Revision 1.2  1996/10/30 10:20:58  keil
+ * X.75 answer of SABMX fixed to response address (AVM X.75 problem)
+ *
+ * Revision 1.1  1996/10/13 20:04:54  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl2.h"
+
+#define TIMER_1 2000
+
+const char *l2_revision = "$Revision: 1.7 $";
+
+static void l2m_debug(struct FsmInst *fi, char *s);
+
+struct Fsm l2fsm =
+{NULL, 0, 0};
+
+enum {
+	ST_L2_1,
+	ST_L2_3,
+	ST_L2_4,
+	ST_L2_5,
+	ST_L2_6,
+	ST_L2_7,
+	ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+static char *strL2State[] =
+{
+	"ST_L2_1",
+	"ST_L2_3",
+	"ST_L2_4",
+	"ST_L2_5",
+	"ST_L2_6",
+	"ST_L2_7",
+	"ST_L2_8",
+};
+
+enum {
+	EV_L2_UI,
+	EV_L2_SABMX,
+	EV_L2_UA,
+	EV_L2_DISC,
+	EV_L2_I,
+	EV_L2_RR,
+	EV_L2_REJ,
+	EV_L2_FRMR,
+	EV_L2_DL_DATA,
+	EV_L2_DL_ESTABLISH,
+	EV_L2_MDL_ASSIGN,
+	EV_L2_MDL_REMOVE,
+	EV_L2_DL_UNIT_DATA,
+	EV_L2_DL_RELEASE,
+	EV_L2_MDL_NOTEIPROC,
+	EV_L2_T200,
+	EV_L2_ACK_PULL,
+	EV_L2_T203,
+	EV_L2_RNR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_RNR+1)
+
+static char *strL2Event[] =
+{
+	"EV_L2_UI",
+	"EV_L2_SABMX",
+	"EV_L2_UA",
+	"EV_L2_DISC",
+	"EV_L2_I",
+	"EV_L2_RR",
+	"EV_L2_REJ",
+	"EV_L2_FRMR",
+	"EV_L2_DL_DATA",
+	"EV_L2_DL_ESTABLISH",
+	"EV_L2_MDL_ASSIGN",
+	"EV_L2_MDL_REMOVE",
+	"EV_L2_DL_UNIT_DATA",
+	"EV_L2_DL_RELEASE",
+	"EV_L2_MDL_NOTEIPROC",
+	"EV_L2_T200",
+	"EV_L2_ACK_PULL",
+	"EV_L2_T203",
+	"EV_L2_RNR",
+};
+
+int errcount = 0;
+
+static int l2addrsize(struct Layer2 *tsp);
+
+static int
+cansend(struct PStack *st)
+{
+	int p1;
+
+	p1 = (st->l2.va + st->l2.window) % (st->l2.extended ? 128 : 8);
+	return (st->l2.vs != p1);
+}
+
+static void
+discard_i_queue(struct PStack *st)
+{
+	struct BufHeader *ibh;
+
+	while (!BufQueueUnlink(&ibh, &st->l2.i_queue))
+		BufPoolRelease(ibh);
+}
+
+int
+l2headersize(struct Layer2 *tsp, int ui)
+{
+	return ((tsp->extended && (!ui) ? 2 : 1) + (tsp->laptype == LAPD ? 2 : 1));
+}
+
+int
+l2addrsize(struct Layer2 *tsp)
+{
+	return (tsp->laptype == LAPD ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *tsp,
+	   struct BufHeader *ibh, int rsp)
+{
+	byte *ptr = DATAPTR(ibh);
+	int crbit;
+
+	if (tsp->laptype == LAPD) {
+		crbit = rsp;
+		if (!tsp->orig)
+			crbit = !crbit;
+		*ptr++ = (tsp->sap << 2) | (crbit ? 2 : 0);
+		*ptr++ = (tsp->tei << 1) | 1;
+		return (2);
+	} else {
+		crbit = rsp;
+		if (tsp->orig)
+			crbit = !crbit;
+		if (crbit)
+			*ptr++ = 1;
+		else
+			*ptr++ = 3;
+		return (1);
+	}
+}
+
+static void
+enqueue_ui(struct PStack *st,
+	   struct BufHeader *ibh)
+{
+	st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static void
+enqueue_super(struct PStack *st,
+	      struct BufHeader *ibh)
+{
+	st->l2.l2l1(st, PH_DATA, ibh);
+}
+
+static int
+legalnr(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int lnr, lvs;
+
+	lvs = (l2->vs >= l2->va) ? l2->vs : (l2->vs + l2->extended ? 128 : 8);
+	lnr = (nr >= l2->va) ? nr : (nr + l2->extended ? 128 : 8);
+	return (lnr <= lvs);
+}
+
+static void
+setva(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+
+	if (l2->va != nr) {
+		while (l2->va != nr) {
+			l2->va = (l2->va + 1) % (l2->extended ? 128 : 8);
+			BufPoolRelease(l2->windowar[l2->sow]);
+			l2->sow = (l2->sow + 1) % l2->window;
+		}
+		if (st->l4.l2writewakeup)
+			st->l4.l2writewakeup(st);
+	}
+}
+
+static void
+l2s1(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.l2tei(st, MDL_ASSIGN, (void *) st->l2.ces);
+	FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2s2(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+
+	byte *ptr;
+	int i;
+
+	i = sethdraddr(&(st->l2), ibh, CMD);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = UI;
+
+	enqueue_ui(st, ibh);
+}
+
+static void
+l2s3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+
+	st->l2.l2l3(st, DL_UNIT_DATA, ibh);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh;
+	int i;
+	byte *ptr;
+
+	FsmChangeState(fi, ST_L2_5);
+	st->l2.rc = 0;
+
+	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 1))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 1");
+
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+		return;
+	i = sethdraddr(&st->l2, ibh, CMD);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	if (st->l2.extended)
+		*ptr = SABME | 0x10;
+	else
+		*ptr = 0x3f;
+	ibh->datasize = i + 1;
+
+	enqueue_super(st, ibh);
+}
+
+static void
+l2s11(struct FsmInst *fi, int event, void *arg)
+{
+	establishlink(fi);
+}
+
+static void
+l2s13(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	byte *ptr;
+	struct BufHeader *ibh;
+	int i;
+
+	FsmChangeState(fi, ST_L2_6);
+
+	FsmDelTimer(&st->l2.t203_timer, 1);
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 2);
+		st->l2.t200_running = 0;
+	}
+	st->l2.rc = 0;
+	if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 2))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 2");
+
+
+	if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+		goto nodisc;
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 9))
+		return;
+	i = sethdraddr(&(st->l2), ibh, CMD);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = DISC | 0x10;
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+      nodisc:
+	discard_i_queue(st);
+}
+
+static void
+l2s12(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	byte *ptr;
+	int i, p;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&(st->l2));
+	p = (*ptr) & 0x10;
+	BufPoolRelease(ibh);
+
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 3");
+
+	st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+		return;
+	i = sethdraddr(&(st->l2), ibh, RSP);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = UA | p;
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+}
+
+static void
+l2s14(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	struct Channel *chanp = st->l4.userdata;
+	byte *ptr;
+	int i, p;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&(st->l2));
+	p = (*ptr) & 0x10;
+	BufPoolRelease(ibh);
+
+	FsmChangeState(fi, ST_L2_4);
+
+	FsmDelTimer(&st->l2.t203_timer, 3);
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 4);
+		st->l2.t200_running = 0;
+	}
+	if ((chanp->impair == 1) && (st->l2.laptype == LAPB))
+		goto noresponse;
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 11))
+		return;
+	i = sethdraddr(&(st->l2), ibh, RSP);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = UA | p;
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+      noresponse:
+	st->l2.l2man(st, DL_RELEASE, NULL);
+
+}
+
+static void
+l2s14_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	struct Channel *chanp = st->l4.userdata;
+	byte *ptr;
+	int i, p;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&(st->l2));
+	p = (*ptr) & 0x10;
+	BufPoolRelease(ibh);
+
+	if ((chanp->impair == 1) && (st->l2.laptype == LAPB))
+		goto noresponse;
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 11))
+		return;
+	i = sethdraddr(&(st->l2), ibh, RSP);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = DM | (p ? 0x10 : 0x0);
+
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+      noresponse:
+
+}
+
+static void
+l2s5(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int f;
+	byte *data;
+
+	data = DATAPTR(ibh);
+	data += l2addrsize(&(st->l2));
+
+	f = *data & 0x10;
+	BufPoolRelease(ibh);
+
+	if (f) {
+		st->l2.vs = 0;
+		st->l2.va = 0;
+		st->l2.vr = 0;
+		st->l2.sow = 0;
+		FsmChangeState(fi, ST_L2_7);
+
+		FsmDelTimer(&st->l2.t200_timer, 5);
+		if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 4))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 4");
+
+
+		st->l2.l2man(st, DL_ESTABLISH, NULL);
+	}
+}
+
+static void
+l2s15(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int f;
+	byte *data;
+
+	data = DATAPTR(ibh);
+	data += l2addrsize(&st->l2);
+
+	f = *data & 0x10;
+	BufPoolRelease(ibh);
+
+	if (f) {
+		FsmDelTimer(&st->l2.t200_timer, 6);
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	}
+}
+
+static void
+enquiry_response(struct PStack *st)
+{
+	struct BufHeader *ibh2;
+	int i;
+	byte *ptr;
+	struct Layer2 *l2;
+
+	l2 = &st->l2;
+	if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 16)) {
+		i = sethdraddr(l2, ibh2, RSP);
+		ptr = DATAPTR(ibh2);
+		ptr += i;
+
+		if (l2->extended) {
+			*ptr++ = RR;
+			*ptr++ = (l2->vr << 1) | 0x1;
+			i += 2;
+		} else {
+			*ptr++ = (l2->vr << 5) | 0x1 | 0x10;
+			i += 1;
+		}
+		ibh2->datasize = i;
+		enqueue_super(st, ibh2);
+	}
+}
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+	/* should log error here */
+	establishlink(fi);
+}
+
+static void
+l2s6(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	struct BufHeader *ibh = arg;
+	int p, seq, rsp;
+	byte *ptr;
+	struct Layer2 *l2;
+
+	l2 = &st->l2;
+	ptr = DATAPTR(ibh);
+
+	if (l2->laptype == LAPD) {
+		rsp = ptr[0] & 0x2;
+		if (l2->orig)
+			rsp = !rsp;
+	} else {
+		rsp = ptr[0] == 0x3;
+		if (l2->orig)
+			rsp = !rsp;
+	}
+
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[1] >> 1;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 5) & 0x7;
+	}
+	BufPoolRelease(ibh);
+
+	if ((chanp->impair == 4) && (st->l2.laptype == LAPB))
+		goto noresp;
+
+	if ((!rsp) && p)
+		enquiry_response(st);
+
+      noresp:
+	if (legalnr(st, seq)) {
+		if (seq == st->l2.vs) {
+			setva(st, seq);
+			FsmDelTimer(&st->l2.t200_timer, 7);
+			st->l2.t200_running = 0;
+			FsmDelTimer(&st->l2.t203_timer, 8);
+			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 5))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 5");
+
+			if (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		} else if (st->l2.va != seq) {
+			setva(st, seq);
+			FsmDelTimer(&st->l2.t200_timer, 9);
+			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 6))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 6");
+
+			if (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		}
+	} else
+		nrerrorrecovery(fi);
+
+	if ((fi->userint & LC_FLUSH_WAIT) && rsp && !l2->i_queue.head) {
+		fi->userint &= ~LC_FLUSH_WAIT;
+		st->l2.l2man(st, DL_FLUSH, NULL);
+	}
+}
+
+static void
+l2s7(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int i;
+	byte *ptr;
+
+	i = sethdraddr(&st->l2, ibh, CMD);
+	ptr = DATAPTR(ibh);
+	BufQueueLink(&st->l2.i_queue, ibh);
+	st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static int
+icommandreceived(struct FsmInst *fi, int event, void *arg, int *nr)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	struct BufHeader *ibh = arg;
+	byte *ptr;
+	struct BufHeader *ibh2;
+	struct IsdnCardState *sp = st->l1.hardware;
+	struct Layer2 *l2 = &(st->l2);
+	int i, p, seq, wasok;
+	char str[64];
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(l2);
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[0] >> 1;
+		*nr = (ptr[1] >> 1) & 0x7f;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 1) & 0x7;
+		*nr = (ptr[0] >> 5) & 0x7;
+	}
+
+	if (l2->vr == seq) {
+		wasok = !0;
+
+		l2->vr = (l2->vr + 1) % (l2->extended ? 128 : 8);
+		l2->rejexp = 0;
+
+		ptr = DATAPTR(ibh);
+		if (st->l2.laptype == LAPD)
+			if (sp->dlogflag) {
+				LogFrame(st->l1.hardware, ptr, ibh->datasize);
+				sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
+				dlogframe(st->l1.hardware, ptr + l2->ihsize,
+					ibh->datasize - l2->ihsize, str);
+			}
+	      label8_1:
+		if ((chanp->impair == 3) && (st->l2.laptype == LAPB))
+			goto noRR;
+
+		if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 13)) {
+			i = sethdraddr(&(st->l2), ibh2, RSP);
+			ptr = DATAPTR(ibh2);
+			ptr += i;
+
+			if (l2->extended) {
+				*ptr++ = RR;
+				*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+				i += 2;
+			} else {
+				*ptr++ = (l2->vr << 5) | 0x1 | (p ? 0x10 : 0x0);
+				i += 1;
+			}
+			ibh2->datasize = i;
+			enqueue_super(st, ibh2);
+		      noRR:
+		}
+	} else {
+		/* n(s)!=v(r) */
+		wasok = 0;
+		BufPoolRelease(ibh);
+		if (st->l2.rejexp) {
+			if (p)
+				goto label8_1;
+		} else {
+			st->l2.rejexp = !0;
+			if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 14)) {
+				i = sethdraddr(&(st->l2), ibh2, RSP);
+				ptr = DATAPTR(ibh2);
+				ptr += i;
+
+				if (l2->extended) {
+					*ptr++ = REJ;
+					*ptr++ = (l2->vr << 1) | (p ? 1 : 0);
+					i += 2;
+				} else {
+					*ptr++ = (l2->vr << 5) | 0x9 | (p ? 0x10 : 0x0);
+					i += 1;
+				}
+				ibh2->datasize = i;
+				enqueue_super(st, ibh2);
+			}
+		}
+	}
+
+	return wasok;
+
+}
+
+static void
+l2s8(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = 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 (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		} else if (nr != st->l2.va) {
+			setva(st, nr);
+			FsmDelTimer(&st->l2.t200_timer, 12);
+			if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 8))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 6");
+
+			if (st->l2.i_queue.head)
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+		}
+	} else
+		nrerrorrecovery(fi);
+
+	if (wasok)
+		st->l2.l2l3(st, DL_DATA, ibh);
+}
+
+static void
+l2s8_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int nr, wasok;
+
+	wasok = icommandreceived(fi, event, arg, &nr);
+
+	if (legalnr(st, nr)) {
+		setva(st, nr);
+		if (st->l2.i_queue.head)
+			st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	} else
+		nrerrorrecovery(fi);
+
+	if (wasok)
+		st->l2.l2l3(st, DL_DATA, ibh);
+}
+
+static void
+l2s17(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.tei = (int) arg;
+	establishlink(fi);
+}
+
+static void
+invoke_retransmission(struct PStack *st, int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int p1;
+
+	if (l2->vs != nr) {
+		while (l2->vs != nr) {
+
+			l2->vs = l2->vs - 1;
+			if (l2->vs < 0)
+				l2->vs += l2->extended ? 128 : 8;
+
+			p1 = l2->vs - l2->va;
+			if (p1 < 0)
+				p1 += l2->extended ? 128 : 8;
+			p1 = (p1 + l2->sow) % l2->window;
+
+			BufQueueLinkFront(&l2->i_queue, l2->windowar[p1]);
+		}
+		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+	}
+}
+
+static void
+l2s16(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int p, seq, rsp;
+	byte *ptr;
+	struct Layer2 *l2;
+
+	l2 = &(st->l2);
+	ptr = DATAPTR(ibh);
+
+	if (l2->laptype == LAPD) {
+		rsp = ptr[0] & 0x2;
+		if (l2->orig)
+			rsp = !rsp;
+	} else {
+		rsp = ptr[0] == 0x3;
+		if (l2->orig)
+			rsp = !rsp;
+	}
+
+
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[1] >> 1;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 5) & 0x7;
+	}
+	BufPoolRelease(ibh);
+
+	if ((!rsp) && p)
+		enquiry_response(st);
+
+	if (!legalnr(st, seq))
+		return;
+
+	setva(st, seq);
+	invoke_retransmission(st, seq);
+
+}
+
+static void
+l2s19(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2s20(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	int i;
+	struct BufHeader *ibh;
+	byte *ptr;
+
+	if (st->l2.rc == st->l2.n200) {
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2tei(st, MDL_VERIFY, (void *) st->l2.tei);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	} else {
+		st->l2.rc++;
+
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 9))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 7");
+
+		if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+			return;
+
+		i = sethdraddr(&st->l2, ibh, CMD);
+		ptr = DATAPTR(ibh);
+		ptr += i;
+		if (st->l2.extended)
+			*ptr = SABME | 0x10;
+		else
+			*ptr = 0x3f;
+		ibh->datasize = i + 1;
+		enqueue_super(st, ibh);
+	}
+}
+
+static void
+l2s21(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct Channel *chanp = st->l4.userdata;
+	int i;
+	struct BufHeader *ibh;
+	byte *ptr;
+
+	if (st->l2.rc == st->l2.n200) {
+		FsmChangeState(fi, ST_L2_4);
+		st->l2.l2man(st, DL_RELEASE, NULL);
+	} else {
+		st->l2.rc++;
+
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 10))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 8");
+
+
+		if ((chanp->impair == 2) && (st->l2.laptype == LAPB))
+			goto nodisc;
+
+		if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 15))
+			return;
+
+		i = sethdraddr(&st->l2, ibh, CMD);
+		ptr = DATAPTR(ibh);
+		ptr += i;
+		*ptr = DISC | 0x10;
+		ibh->datasize = i + 1;
+		enqueue_super(st, ibh);
+	      nodisc:
+
+	}
+}
+
+static void
+l2s22(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh;
+	struct Layer2 *l2 = &st->l2;
+	byte *ptr;
+	int p1;
+
+	if (!cansend(st))
+		return;
+
+	if (BufQueueUnlink(&ibh, &l2->i_queue))
+		return;
+
+
+	p1 = l2->vs - l2->va;
+	if (p1 < 0)
+		p1 += l2->extended ? 128 : 8;
+	p1 = (p1 + l2->sow) % l2->window;
+	l2->windowar[p1] = ibh;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		*ptr++ = l2->vs << 1;
+		*ptr++ = l2->vr << 1;
+		l2->vs = (l2->vs + 1) % 128;
+	} else {
+		*ptr++ = (l2->vr << 5) | (l2->vs << 1);
+		l2->vs = (l2->vs + 1) % 8;
+	}
+
+	st->l2.l2l1(st, PH_DATA_PULLED, ibh);
+
+	if (!st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t203_timer, 13);
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 11))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 9");
+
+		st->l2.t200_running = !0;
+	}
+	if (l2->i_queue.head && cansend(st))
+		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static void
+transmit_enquiry(struct PStack *st)
+{
+	struct BufHeader *ibh;
+	byte *ptr;
+
+	if (!BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 12)) {
+		ptr = DATAPTR(ibh);
+		ptr += sethdraddr(&st->l2, ibh, CMD);
+
+		if (st->l2.extended) {
+			*ptr++ = RR;
+			*ptr++ = (st->l2.vr << 1) | 1;
+		} else {
+			*ptr++ = (st->l2.vr << 5) | 0x11;
+		}
+		ibh->datasize = ptr - DATAPTR(ibh);
+		enqueue_super(st, ibh);
+		if (FsmAddTimer(&st->l2.t200_timer, st->l2.t200, EV_L2_T200, NULL, 12))
+			if (st->l2.l2m.debug)
+				l2m_debug(&st->l2.l2m, "FAT 10");
+
+		st->l2.t200_running = !0;
+	}
+}
+
+static void
+l2s23(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.t200_running = 0;
+
+	st->l2.rc = 1;
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+}
+
+static void
+l2s24(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	int p, seq, rsp;
+	byte *ptr;
+	struct Layer2 *l2;
+
+	l2 = &st->l2;
+	ptr = DATAPTR(ibh);
+
+	if (l2->laptype == LAPD) {
+		rsp = ptr[0] & 0x2;
+		if (l2->orig)
+			rsp = !rsp;
+	} else {
+		rsp = ptr[0] == 0x3;
+		if (l2->orig)
+			rsp = !rsp;
+	}
+
+
+	ptr += l2addrsize(l2);
+
+	if (l2->extended) {
+		p = (ptr[1] & 0x1) == 0x1;
+		seq = ptr[1] >> 1;
+	} else {
+		p = (ptr[0] & 0x10);
+		seq = (ptr[0] >> 5) & 0x7;
+	}
+	BufPoolRelease(ibh);
+
+	if (rsp && p) {
+		if (legalnr(st, seq)) {
+			FsmChangeState(fi, ST_L2_7);
+			setva(st, seq);
+			if (st->l2.t200_running) {
+				FsmDelTimer(&st->l2.t200_timer, 14);
+				st->l2.t200_running = 0;
+			}
+			if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 13))
+				if (st->l2.l2m.debug)
+					l2m_debug(&st->l2.l2m, "FAT 11");
+
+			invoke_retransmission(st, seq);
+
+			if (l2->i_queue.head && cansend(st))
+				st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+			else if (fi->userint & LC_FLUSH_WAIT) {
+				fi->userint &= ~LC_FLUSH_WAIT;
+				st->l2.l2man(st, DL_FLUSH, NULL);
+			}
+		}
+	} else {
+		if (!rsp && p)
+			enquiry_response(st);
+		if (legalnr(st, seq)) {
+			setva(st, seq);
+		}
+	}
+}
+
+static void
+l2s25(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.rc = 0;
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+}
+
+static void
+l2s26(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (st->l2.rc == st->l2.n200) {
+		establishlink(fi);
+	} else {
+		st->l2.rc++;
+		transmit_enquiry(st);
+	}
+}
+
+static void
+l2s27(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	byte *ptr;
+	int i, p, est;
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&st->l2);
+
+	p = ptr[0] & 0x10;
+
+	BufPoolRelease(ibh);
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
+		return;
+	i = sethdraddr(&st->l2, ibh, RSP);
+	ptr = DATAPTR(ibh);
+	ptr += i;
+	*ptr = UA | p;
+	ibh->datasize = i + 1;
+	enqueue_super(st, ibh);
+
+	if (st->l2.vs != st->l2.va) {
+		discard_i_queue(st);
+		est = !0;
+	} else
+		est = 0;
+
+	FsmDelTimer(&st->l2.t200_timer, 15);
+	st->l2.t200_running = 0;
+
+	if (FsmAddTimer(&st->l2.t203_timer, st->l2.t203, EV_L2_T203, NULL, 3))
+		if (st->l2.l2m.debug)
+			l2m_debug(&st->l2.l2m, "FAT 12");
+
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+
+
+	if (est)
+		st->l2.l2man(st, DL_ESTABLISH, NULL);
+
+}
+
+static void
+l2s27_1(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L2_7);
+	l2s27(fi, event, arg);
+
+	if (st->l2.i_queue.head && cansend(st))
+		st->l2.l2l1(st, PH_REQUEST_PULL, NULL);
+}
+
+static void
+l2s28(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct BufHeader *ibh = arg;
+	byte *ptr;
+	char tmp[64];
+
+	ptr = DATAPTR(ibh);
+	ptr += l2addrsize(&st->l2);
+	ptr++;
+
+	if (st->l2.l2m.debug) {
+		if (st->l2.extended)
+			sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
+				ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
+		else
+			sprintf(tmp, "FRMR information %2x %2x %2x",
+				ptr[0], ptr[1], ptr[2]);
+
+		l2m_debug(&st->l2.l2m, tmp);
+	}
+	BufPoolRelease(ibh);
+}
+
+static void
+l2s30(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+/*TODO
+   if( DL_RELEASE.req outstanding ) {
+   ... issue DL_RELEASE.confirm
+   } else {
+   if( fi->state != ST_L2_4 ) {
+   ... issue DL_RELEASE.indication
+   }
+   }
+   TODO */
+	discard_i_queue(st);	/* There is no UI queue in layer 2 */
+	st->l2.tei = 255;
+	if (st->l2.t200_running) {
+		FsmDelTimer(&st->l2.t200_timer, 18);
+		st->l2.t200_running = 0;
+	}
+	FsmDelTimer(&st->l2.t203_timer, 19);
+	st->l2.l2man(st, DL_RELEASE, NULL);	/* TEMP */
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static int
+IsUI(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == UI);
+}
+
+static int
+IsUA(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == UA);
+}
+
+static int
+IsDISC(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == DISC);
+}
+
+static int
+IsRR(byte * data, int ext)
+{
+	if (ext)
+		return (data[0] == RR);
+	else
+		return ((data[0] & 0xf) == 1);
+}
+
+static int
+IsI(byte * data, int ext)
+{
+	return ((data[0] & 0x1) == 0x0);
+}
+
+static int
+IsSABMX(byte * data, int ext)
+{
+	return (ext ? (data[0] & ~0x10) == SABME : data[0] == 0x3f);
+}
+
+static int
+IsREJ(byte * data, int ext)
+{
+	return (ext ? data[0] == REJ : (data[0] & 0xf) == 0x9);
+}
+
+static int
+IsFRMR(byte * data, int ext)
+{
+	return ((data[0] & 0xef) == FRMR);
+}
+
+static int
+IsRNR(byte * data, int ext)
+{
+	if (ext)
+		return (data[0] == RNR);
+	else
+		return ((data[0] & 0xf) == 5);
+}
+
+static struct FsmNode L2FnList[] =
+{
+	{ST_L2_1, EV_L2_DL_ESTABLISH, l2s1},
+	{ST_L2_1, EV_L2_MDL_NOTEIPROC, l2s19},
+	{ST_L2_3, EV_L2_MDL_ASSIGN, l2s17},
+	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2s2},
+	{ST_L2_4, EV_L2_DL_ESTABLISH, l2s11},
+	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2s2},
+	{ST_L2_7, EV_L2_DL_DATA, l2s7},
+	{ST_L2_7, EV_L2_DL_RELEASE, l2s13},
+	{ST_L2_7, EV_L2_ACK_PULL, l2s22},
+	{ST_L2_8, EV_L2_DL_DATA, l2s7},
+	{ST_L2_8, EV_L2_DL_RELEASE, l2s13},
+
+	{ST_L2_1, EV_L2_UI, l2s3},
+	{ST_L2_4, EV_L2_UI, l2s3},
+	{ST_L2_4, EV_L2_SABMX, l2s12},
+	{ST_L2_4, EV_L2_DISC, l2s14_1},
+	{ST_L2_5, EV_L2_UA, l2s5},
+	{ST_L2_6, EV_L2_UA, l2s15},
+	{ST_L2_7, EV_L2_UI, l2s3},
+	{ST_L2_7, EV_L2_DISC, l2s14},
+	{ST_L2_7, EV_L2_I, l2s8},
+	{ST_L2_7, EV_L2_RR, l2s6},
+	{ST_L2_7, EV_L2_REJ, l2s16},
+	{ST_L2_7, EV_L2_SABMX, l2s27},
+	{ST_L2_7, EV_L2_FRMR, l2s28},
+	{ST_L2_8, EV_L2_RR, l2s24},
+	{ST_L2_8, EV_L2_REJ, l2s24},
+	{ST_L2_8, EV_L2_SABMX, l2s27_1},
+	{ST_L2_8, EV_L2_DISC, l2s14},
+	{ST_L2_8, EV_L2_FRMR, l2s28},
+	{ST_L2_8, EV_L2_I, l2s8_1},
+
+	{ST_L2_5, EV_L2_T200, l2s20},
+	{ST_L2_6, EV_L2_T200, l2s21},
+	{ST_L2_7, EV_L2_T200, l2s23},
+	{ST_L2_7, EV_L2_T203, l2s25},
+	{ST_L2_8, EV_L2_T200, l2s26},
+
+	{ST_L2_1, EV_L2_MDL_REMOVE, l2s30},
+/*      {ST_L2_2, EV_L2_MDL_REMOVE, l2s30 }, */
+	{ST_L2_3, EV_L2_MDL_REMOVE, l2s30},
+	{ST_L2_4, EV_L2_MDL_REMOVE, l2s30},
+	{ST_L2_5, EV_L2_MDL_REMOVE, l2s30},
+	{ST_L2_6, EV_L2_MDL_REMOVE, l2s30},
+	{ST_L2_7, EV_L2_MDL_REMOVE, l2s30},
+	{ST_L2_8, EV_L2_MDL_REMOVE, l2s30},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static void
+isdnl2_l1l2(struct PStack *st, int pr, struct BufHeader *arg)
+{
+	struct BufHeader *ibh;
+	byte *datap;
+	int ret = !0;
+
+	switch (pr) {
+		case (PH_DATA):
+
+			ibh = arg;
+			datap = DATAPTR(ibh);
+			datap += l2addrsize(&st->l2);
+
+			if (IsI(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_I, ibh);
+			else if (IsRR(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_RR, ibh);
+			else if (IsUI(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_UI, ibh);
+			else if (IsSABMX(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_SABMX, ibh);
+			else if (IsUA(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_UA, ibh);
+			else if (IsDISC(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, ibh);
+			else if (IsREJ(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_REJ, ibh);
+			else if (IsFRMR(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, ibh);
+			else if (IsRNR(datap, st->l2.extended))
+				ret = FsmEvent(&st->l2.l2m, EV_L2_RNR, ibh);
+
+			if (ret)
+				BufPoolRelease(ibh);
+
+			break;
+		case (PH_PULL_ACK):
+			FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+			break;
+	}
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr,
+	    void *arg)
+{
+	switch (pr) {
+		case (DL_DATA):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg))
+				BufPoolRelease((struct BufHeader *) arg);
+			break;
+		case (DL_UNIT_DATA):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg))
+				BufPoolRelease((struct BufHeader *) arg);
+			break;
+	}
+}
+
+static void
+isdnl2_manl2(struct PStack *st, int pr,
+	     void *arg)
+{
+	switch (pr) {
+		case (DL_ESTABLISH):
+			FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+			break;
+		case (DL_RELEASE):
+			FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
+			break;
+		case (MDL_NOTEIPROC):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
+			break;
+		case (DL_FLUSH):
+			(&st->l2.l2m)->userint |= LC_FLUSH_WAIT;
+			break;
+	}
+}
+
+static void
+isdnl2_teil2(struct PStack *st, int pr,
+	     void *arg)
+{
+	switch (pr) {
+		case (MDL_ASSIGN):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+			break;
+		case (MDL_REMOVE):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
+			break;
+	}
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+	FsmDelTimer(&st->l2.t200_timer, 15);
+	FsmDelTimer(&st->l2.t203_timer, 16);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *s)
+{
+	struct PStack *st = fi->userdata;
+	char tm[32], str[256];
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
+	HiSax_putstatus(st->l1.hardware, str);
+}
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+	st->l1.l1l2 = isdnl2_l1l2;
+	st->l3.l3l2 = isdnl2_l3l2;
+	st->ma.manl2 = isdnl2_manl2;
+	st->ma.teil2 = isdnl2_teil2;
+
+	st->l2.uihsize = l2headersize(&st->l2, !0);
+	st->l2.ihsize = l2headersize(&st->l2, 0);
+	BufQueueInit(&(st->l2.i_queue));
+	st->l2.rejexp = 0;
+	st->l2.debug = 0;
+
+	st->l2.l2m.fsm = &l2fsm;
+	st->l2.l2m.state = ST_L2_1;
+	st->l2.l2m.debug = 0;
+	st->l2.l2m.userdata = st;
+	st->l2.l2m.userint = 0;
+	st->l2.l2m.printdebug = l2m_debug;
+	strcpy(st->l2.debug_id, debug_id);
+
+	FsmInitTimer(&st->l2.l2m, &st->l2.t200_timer);
+	FsmInitTimer(&st->l2.l2m, &st->l2.t203_timer);
+	st->l2.t200_running = 0;
+}
+
+void
+setstack_transl2(struct PStack *st)
+{
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+void
+Isdnl2New(void)
+{
+	l2fsm.state_count = L2_STATE_COUNT;
+	l2fsm.event_count = L2_EVENT_COUNT;
+	l2fsm.strEvent = strL2Event;
+	l2fsm.strState = strL2State;
+	FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+	FsmFree(&l2fsm);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/isdnl2.h linux/drivers/isdn/hisax/isdnl2.h
--- v2.1.26/linux/drivers/isdn/hisax/isdnl2.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl2.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,17 @@
+/* isdnl2.h */
+
+#define RR     0x01
+#define RNR    0x05
+#define REJ    0x09
+#define SABME  0x6f
+#define DM     0x0f
+#define UI     0x03
+#define DISC   0x43
+#define UA     0x63
+#define FRMR   0x87
+#define XID    0xaf
+
+#define CMD    0
+#define RSP    1
+
+#define LC_FLUSH_WAIT 1
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/isdnl3.c linux/drivers/isdn/hisax/isdnl3.c
--- v2.1.26/linux/drivers/isdn/hisax/isdnl3.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl3.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,162 @@
+/* $Id: isdnl3.c,v 1.6 1997/02/16 01:04:08 fritz Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: isdnl3.c,v $
+ * Revision 1.6  1997/02/16 01:04:08  fritz
+ * Bugfix: Changed timer handling caused hang with 2.1.X
+ *
+ * Revision 1.5  1997/02/09 00:26:27  keil
+ * new interface handling, one interface per card
+ * leased line changes
+ *
+ * Revision 1.4  1997/01/27 23:17:44  keil
+ * delete timers while unloading
+ *
+ * Revision 1.3  1997/01/21 22:31:12  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.2  1996/11/05 19:42:04  keil
+ * using config.h
+ *
+ * Revision 1.1  1996/10/13 20:04:54  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl3.h"
+#include <linux/config.h>
+
+const char *l3_revision = "$Revision: 1.6 $";
+
+void
+l3_debug(struct PStack *st, char *s)
+{
+	char str[256], tm[32];
+
+	jiftime(tm, jiffies);
+	sprintf(str, "%s Channel %d l3 %s\n", tm, st->l3.channr, s);
+	HiSax_putstatus(st->l1.hardware, str);
+}
+
+
+
+void
+newl3state(struct PStack *st, int state)
+{
+	char tmp[80];
+
+	if (st->l3.debug & L3_DEB_STATE) {
+		sprintf(tmp, "newstate  %d --> %d", st->l3.state, state);
+		l3_debug(st, tmp);
+	}
+	st->l3.state = state;
+}
+
+static void
+L3ExpireTimer(struct L3Timer *t)
+{
+	t->st->l4.l4l3(t->st, t->event, NULL);
+}
+
+void
+L3InitTimer(struct PStack *st, struct L3Timer *t)
+{
+	t->st = st;
+	t->tl.function = (void *) L3ExpireTimer;
+	t->tl.data = (long) t;
+	init_timer(&t->tl);
+}
+
+void
+L3DelTimer(struct L3Timer *t)
+{
+	del_timer(&t->tl);
+}
+
+int
+L3AddTimer(struct L3Timer *t,
+	   int millisec, int event)
+{
+	if (t->tl.next || t->tl.prev) {
+		printk(KERN_WARNING "L3AddTimer: timer already active!\n");
+		return -1;
+	}
+	init_timer(&t->tl);
+	t->event = event;
+	t->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&t->tl);
+	return 0;
+}
+
+void
+StopAllL3Timer(struct PStack *st)
+{
+	L3DelTimer(&st->l3.timer);
+}
+
+static void
+no_l3_proto(struct PStack *st, int pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	l3_debug(st, "no protocol");
+	if (ibh)
+		BufPoolRelease(ibh);
+}
+
+#ifdef	CONFIG_HISAX_EURO
+extern void setstack_dss1(struct PStack *st);
+#endif
+
+#ifdef	CONFIG_HISAX_1TR6
+extern void setstack_1tr6(struct PStack *st);
+#endif
+
+void
+setstack_isdnl3(struct PStack *st, struct Channel *chanp)
+{
+	char tmp[64];
+
+	st->l3.debug = L3_DEB_WARN;
+	st->l3.channr = chanp->chan;
+	L3InitTimer(st, &st->l3.timer);
+
+#ifdef	CONFIG_HISAX_EURO
+	if (st->protocol == ISDN_PTYPE_EURO) {
+		setstack_dss1(st);
+	} else
+#endif
+#ifdef	CONFIG_HISAX_1TR6
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		setstack_1tr6(st);
+	} else
+#endif
+	if (st->protocol == ISDN_PTYPE_LEASED) {
+		st->l4.l4l3 = no_l3_proto;
+		st->l2.l2l3 = no_l3_proto;
+		printk(KERN_NOTICE "HiSax: Leased line mode\n");
+	} else {
+		sprintf(tmp, "protocol %s not supported",
+			(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
+			(st->protocol == ISDN_PTYPE_EURO) ? "euro" :
+			"unknown");
+		l3_debug(st, tmp);
+		st->protocol = -1;
+	}
+	st->l3.state = 0;
+	st->l3.callref = 0;
+}
+
+void
+releasestack_isdnl3(struct PStack *st)
+{
+	StopAllL3Timer(st);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/isdnl3.h linux/drivers/isdn/hisax/isdnl3.h
--- v2.1.26/linux/drivers/isdn/hisax/isdnl3.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/isdnl3.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,34 @@
+/* $Id: isdnl3.h,v 1.2 1997/01/21 22:31:28 keil Exp $
+ *
+ * $Log: isdnl3.h,v $
+ * Revision 1.2  1997/01/21 22:31:28  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.1  1996/10/13 20:03:47  keil
+ * Initial revision
+ *
+ *
+ */
+
+#define SBIT(state) (1<<state)
+#define ALL_STATES  0x00ffffff
+
+#define	PROTO_DIS_EURO	0x08
+
+#define L3_DEB_WARN	0x01
+#define	L3_DEB_PROTERR	0x02
+#define	L3_DEB_STATE	0x04
+#define	L3_DEB_CHARGE	0x08
+
+struct stateentry {
+	int             state;
+	byte            primitive;
+	void            (*rout) (struct PStack *, byte, void *);
+};
+
+extern void l3_debug(struct PStack *st, char *s);
+extern void newl3state(struct PStack *st, int state);
+extern void L3InitTimer(struct PStack *st, struct L3Timer *t);
+extern void L3DelTimer(struct L3Timer *t);
+extern int  L3AddTimer(struct L3Timer *t, int millisec, int event);
+extern void StopAllL3Timer(struct PStack *st);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/ix1_micro.c linux/drivers/isdn/hisax/ix1_micro.c
--- v2.1.26/linux/drivers/isdn/hisax/ix1_micro.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/ix1_micro.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,955 @@
+/* $Id: ix1_micro.c,v 1.1 1997/01/27 15:43:10 keil Exp $
+
+ * ix1_micro.c  low level stuff for ITK ix1-micro Rev.2 isdn cards
+ *              derived from the original file teles3.c from Karsten Keil
+ *
+ * Copyright (C) 1997 Klaus-Peter Nischke (ITK AG) (for the modifications to
+ *                                                  the original file teles.c)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ * $Log: ix1_micro.c,v $
+ * Revision 1.1  1997/01/27 15:43:10  keil
+ * first version
+ *
+ *
+ */
+
+/*
+   For the modification done by the author the following terms and conditions
+   apply (GNU PUBLIC LICENSE)
+
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+   You may contact Klaus-Peter Nischke by email: klaus@nischke.do.eunet.de
+   or by conventional mail:
+
+   Klaus-Peter Nischke
+   Deusener Str. 287
+   44369 Dortmund
+   Germany
+ */
+
+
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "teles3.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+const char *ix1_revision = "$Revision: 1.1 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+#define SPECIAL_PORT_OFFSET 3
+
+#define ISAC_COMMAND_OFFSET 2
+#define ISAC_DATA_OFFSET 0
+#define HSCX_COMMAND_OFFSET 2
+#define HSCX_DATA_OFFSET 1
+
+#define ISAC_FIFOSIZE 16
+#define HSCX_FIFOSIZE 16
+
+#define TIMEOUT 50
+
+static inline byte
+IsacReadReg(unsigned int adr, byte off)
+{
+	byteout(adr + ISAC_COMMAND_OFFSET, off + 0x20);
+	return bytein(adr + ISAC_DATA_OFFSET);
+}
+
+static inline void
+IsacWriteReg(unsigned int adr, byte off, byte 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 byte
+HscxReadReg(unsigned int adr, int WhichHscx, byte 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, byte off, byte data)
+{
+	byteout(adr + HSCX_COMMAND_OFFSET, HSCX_OFFSET(WhichHscx, off));
+	byteout(adr + HSCX_DATA_OFFSET, data);
+}
+
+
+static inline void
+IsacReadFifo(unsigned int adr, byte * data, int size)
+{
+	byteout(adr + ISAC_COMMAND_OFFSET, 0);
+	while (size--)
+		*data++ = bytein(adr + ISAC_DATA_OFFSET);
+}
+
+static void
+IsacWriteFifo(unsigned int adr, byte * 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, byte * data, int size)
+{
+	byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00);
+	while (size--)
+		*data++ = bytein(adr + HSCX_DATA_OFFSET);
+}
+
+static void
+HscxWriteFifo(unsigned int adr, int WhichHscx, byte * data, int size)
+{
+	byteout(adr + HSCX_COMMAND_OFFSET, (WhichHscx) ? 0x40 : 0x00);
+	while (size--) {
+		byteout(adr + HSCX_DATA_OFFSET, *data);
+		data++;
+	}
+}
+
+static inline void
+waitforCEC(int adr, int WhichHscx)
+{
+	int to = TIMEOUT;
+
+	while ((HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "ix1-Micro: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr, int WhichHscx)
+{
+	int to = TIMEOUT;
+
+	while ((!(HscxReadReg(adr, WhichHscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "ix1-Micro: waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR(int adr, int WhichHscx, byte data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr, WhichHscx);
+	HscxWriteReg(adr, WhichHscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", HscxReadReg(sp->hscx[hscx], hscx, HSCX_EXIR));
+}
+
+void
+ix1micro_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", IsacReadReg(sp->isac, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", IsacReadReg(sp->isac, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", IsacReadReg(sp->isac, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	byte *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh = hsp->rcvibh;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+					      HSCX_RBUF_BPPS)) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->rcvptr;
+
+	hsp->rcvptr += count;
+	save_flags(flags);
+	cli();
+	HscxReadFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh;
+	int more, count;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	ibh = hsp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - hsp->sendptr;
+	if (count <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->sendptr;
+	hsp->sendptr += count;
+
+	waitforXFW(sp->hscx[hsp->hscx], hsp->hscx);
+	save_flags(flags);
+	cli();
+	HscxWriteFifo(sp->hscx[hsp->hscx], hsp->hscx, ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *sp, byte val, byte hscx)
+{
+	byte r;
+	struct HscxState *hsp = sp->hs + hscx;
+	int count, err;
+	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");
+			if (hsp->rcvibh)
+				BufPoolRelease(hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+			goto afterRME;
+		}
+		if (!hsp->rcvibh)
+			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+				       GFP_ATOMIC, (void *) 1, 1)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RME out of buffers");
+				writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+				goto afterRME;
+			} else
+				hsp->rcvptr = 0;
+
+		count = HscxReadReg(sp->hscx[hsp->hscx], hscx, HSCX_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		hscx_empty_fifo(hsp, count);
+		hsp->rcvibh->datasize = hsp->rcvptr - 1;
+		BufQueueLink(&hsp->rq, hsp->rcvibh);
+		hsp->rcvibh = NULL;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!hsp->rcvibh) {
+			if (hsp->mode == 1)
+				err = BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+			else
+				err = BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+
+			if (err) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RPF out of buffers");
+				writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x80);
+				goto afterRPF;
+			} else
+				hsp->rcvptr = 0;
+		}
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			hsp->rcvibh->datasize = hsp->rcvptr;
+			BufQueueLink(&hsp->rq, hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+      afterRPF:
+	if (val & 0x10) {	/* XPR */
+		if (hsp->xmtibh)
+			if (hsp->xmtibh->datasize > hsp->sendptr) {
+				hscx_fill_fifo(hsp);
+				goto afterXPR;
+			} else {
+				if (hsp->releasebuf)
+					BufPoolRelease(hsp->xmtibh);
+				hsp->sendptr = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->xmtibh = NULL;
+			}
+		if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+			hsp->releasebuf = !0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+      afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	byte *ptr;
+	struct BufHeader *ibh = sp->rcvibh;
+	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->rcvptr >= 3072) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo rcvptr %d", sp->rcvptr);
+			debugl1(sp, tmp);
+		}
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->rcvptr;
+	sp->rcvptr += count;
+
+	save_flags(flags);
+	cli();
+	IsacReadFifo(sp->isac, ptr, count);
+	IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh;
+	int count, more;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	ibh = sp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - sp->sendptr;
+	if (count <= 0)
+		return;
+	if (count >= 3072)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->sendptr;
+	sp->sendptr += count;
+
+	save_flags(flags);
+	cli();
+	IsacWriteFifo(sp->isac, ptr, count);
+	IsacWriteReg(sp->isac, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	IsacWriteReg(sp->isac, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, byte val)
+{
+	byte exval;
+	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");
+			if (sp->rcvibh)
+				BufPoolRelease(sp->rcvibh);
+			sp->rcvibh = NULL;
+			IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
+			goto afterRME;
+		}
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 3)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
+				goto afterRME;
+			} else
+				sp->rcvptr = 0;
+		count = IsacReadReg(sp->isac, ISAC_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		isac_empty_fifo(sp, count);
+		sp->rcvibh->datasize = sp->rcvptr;
+		BufQueueLink(&(sp->rq), sp->rcvibh);
+		sp->rcvibh = NULL;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 4)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				IsacWriteReg(sp->isac, ISAC_CMDR, 0x80);
+				goto afterRPF;
+			} else
+				sp->rcvptr = 0;
+		isac_empty_fifo(sp, 32);
+	}
+      afterRPF:
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->xmtibh)
+			if (sp->xmtibh->datasize > sp->sendptr) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				if (sp->releasebuf)
+					BufPoolRelease(sp->xmtibh);
+				sp->xmtibh = NULL;
+				sp->sendptr = 0;
+			}
+		if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+			sp->releasebuf = !0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (IsacReadReg(sp->isac, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = IsacReadReg(sp->isac, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, byte val)
+{
+
+	byte 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.
+				 */
+				hsp->sendptr = 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.
+				 */
+				hsp->sendptr = 0;
+				writehscxCMDR(sp->hscx[hsp->hscx], hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	byte val, stat = 0;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+		return;
+	}
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+		stat |= 1;
+	}
+	val = IsacReadReg(sp->isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+		stat |= 2;
+	}
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0xFF);
+		HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0xFF);
+		HscxWriteReg(sp->hscx[0], 0, HSCX_MASK, 0x0);
+		HscxWriteReg(sp->hscx[1], 1, HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		IsacWriteReg(sp->isac, ISAC_MASK, 0xFF);
+		IsacWriteReg(sp->isac, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->isac;
+
+	/* 16.3 IOM 2 Mode */
+	IsacWriteReg(adr, ISAC_MASK, 0xff);
+	IsacWriteReg(adr, ISAC_ADF2, 0x80);
+	IsacWriteReg(adr, ISAC_SQXR, 0x2f);
+	IsacWriteReg(adr, ISAC_SPCR, 0x0);
+	IsacWriteReg(adr, ISAC_ADF1, 0x2);
+	IsacWriteReg(adr, ISAC_STCR, 0x70);
+	IsacWriteReg(adr, ISAC_MODE, 0xc9);
+	IsacWriteReg(adr, ISAC_TIMR, 0x0);
+	IsacWriteReg(adr, ISAC_ADF1, 0x0);
+	IsacWriteReg(adr, ISAC_CMDR, 0x41);
+	IsacWriteReg(adr, ISAC_CIX0, (1 << 2) | 3);
+	IsacWriteReg(adr, ISAC_MASK, 0xff);
+	IsacWriteReg(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR1, 0x85);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD1, 0xFF);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XAD2, 0xFF);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RAH2, 0xFF);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XBCH, 0x0);
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RLCR, 0x0);
+
+	switch (mode) {
+	case 0:
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0xff);
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0xff);
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x84);
+		break;
+	case 1:
+		if (ichan == 0) {
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+		} else {
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+		}
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0xe4);
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41);
+		break;
+	case 2:
+		if (ichan == 0) {
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x2f);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x2f);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+		} else {
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CCR2, 0x30);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAX, 0x3);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_TSAR, 0x3);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_XCCR, 7);
+			HscxWriteReg(sp->hscx[hscx], hscx, HSCX_RCCR, 7);
+		}
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_MODE, 0x8c);
+		HscxWriteReg(sp->hscx[hscx], hscx, HSCX_CMDR, 0x41);
+		break;
+	}
+	HscxWriteReg(sp->hscx[hscx], hscx, HSCX_ISTA, 0x00);
+}
+
+void
+release_io_ix1micro(struct IsdnCard *card)
+{
+	if (card->sp->cfg_reg)
+		release_region(card->sp->cfg_reg, 4);
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = HscxReadReg(sp->hscx[1], 1, HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = HscxReadReg(sp->hscx[0], 0, HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = HscxReadReg(sp->hscx[0], 0, HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = HscxReadReg(sp->hscx[1], 1, HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = HscxReadReg(sp->hscx[0], 0, HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = IsacReadReg(sp->isac, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = IsacReadReg(sp->isac, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = IsacReadReg(sp->isac, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	IsacWriteReg(sp->isac, ISAC_MASK, 0);
+	IsacWriteReg(sp->isac, ISAC_CMDR, 0x41);
+}
+
+int
+initix1micro(struct IsdnCardState *sp)
+{
+	int ret;
+	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);
+		sprintf(tmp, "IRQ %d count %d", sp->irq,
+			kstat.interrupts[sp->irq]);
+		debugl1(sp, tmp);
+		if (kstat.interrupts[sp->irq] == sp->counter) {
+			printk(KERN_WARNING
+			       "ix1-Micro: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_ix1micro(struct IsdnCard *card)
+{
+	byte val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, ix1_revision);
+	printk(KERN_NOTICE "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (sp->typ != ISDN_CTYPE_IX1MICROR2)
+		return (0);
+
+	/* IO-Ports */
+	sp->isac = sp->hscx[0] = sp->hscx[1] = sp->cfg_reg = card->para[1];
+	sp->irq = card->para[0];
+	if (sp->cfg_reg) {
+		if (check_region((sp->cfg_reg), 4)) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       sp->cfg_reg,
+			       sp->cfg_reg + 4);
+			return (0);
+		} else
+			request_region(sp->cfg_reg, 4, "ix1micro cfg");
+	}
+	/* reset isac */
+	save_flags(flags);
+	val = 3 * (HZ / 10) + 1;
+	sti();
+	while (val--) {
+		byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 1);
+		HZDELAY(1);	/* wait >=10 ms */
+	}
+	byteout(sp->cfg_reg + SPECIAL_PORT_OFFSET, 0);
+	restore_flags(flags);
+
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d io:0x%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->cfg_reg);
+	verA = HscxReadReg(sp->hscx[0], 0, HSCX_VSTR) & 0xf;
+	verB = HscxReadReg(sp->hscx[1], 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "ix1-Micro: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = IsacReadReg(sp->isac, ISAC_RBCH);
+	printk(KERN_INFO "ix1-Micro: ISAC %s\n",
+	       ISACVersion(val));
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		    "ix1-Micro: wrong HSCX versions check IO address\n");
+		release_io_ix1micro(card);
+		return (0);
+	}
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/ix1_micro.h linux/drivers/isdn/hisax/ix1_micro.h
--- v2.1.26/linux/drivers/isdn/hisax/ix1_micro.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/ix1_micro.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,50 @@
+/* $Id: ix1_micro.h,v 1.1 1997/01/27 15:42:48 keil Exp $
+
+ * ix1_micro.h  low level stuff for ITK ix1-micro Rev.2 isdn cards
+ *
+ *              derived from teles3.h from Karsten Keil
+ *
+ * Copyright (C) 1997 Klaus-Peter Nischke (ITK AG)   (for the modifications
+ to the original file teles.c)
+ *
+ * $Log: ix1_micro.h,v $
+ * Revision 1.1  1997/01/27 15:42:48  keil
+ * first version
+ *
+ *
+ */
+
+/*
+   For the modification done by the author the following terms and conditions
+   apply (GNU PUBLIC LICENSE)
+
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+   You may contact Klaus-Peter Nischke by email: klaus@nischke.do.eunet.de
+   or by conventional mail:
+
+   Klaus-Peter Nischke
+   Deusener Str. 287
+   44369 Dortmund
+   Germany
+ */
+
+
+extern void ix1micro_report(struct IsdnCardState *sp);
+extern void release_io_ix1micro(struct IsdnCard *card);
+extern int setup_ix1micro(struct IsdnCard *card);
+extern int initix1micro(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/l3_1tr6.c linux/drivers/isdn/hisax/l3_1tr6.c
--- v2.1.26/linux/drivers/isdn/hisax/l3_1tr6.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3_1tr6.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,784 @@
+/* $Id: l3_1tr6.c,v 1.9 1997/02/11 01:37:40 keil Exp $
+
+ *  German 1TR6 D-channel protocol
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: l3_1tr6.c,v $
+ * Revision 1.9  1997/02/11 01:37:40  keil
+ * Changed setup-interface (incoming and outgoing)
+ *
+ * Revision 1.8  1997/01/27 23:20:21  keil
+ * report revision only ones
+ *
+ * Revision 1.7  1997/01/21 22:30:07  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.6  1996/12/14 21:07:20  keil
+ * additional states for CC_REJECT
+ *
+ * Revision 1.5  1996/12/08 19:55:17  keil
+ * change CC_REJECT_REQ routine
+ *
+ * Revision 1.4  1996/10/30 10:18:01  keil
+ * bugfixes in debugging output
+ *
+ * Revision 1.3  1996/10/27 22:15:37  keil
+ * bugfix reject handling
+ *
+ * Revision 1.2  1996/10/13 23:08:56  keil
+ * added missing state for callback reject
+ *
+ * Revision 1.1  1996/10/13 20:04:55  keil
+ * Initial revision
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "l3_1tr6.h"
+#include "isdnl3.h"
+
+extern char *HiSax_getrev(const char *revision);
+const char *l3_1tr6_revision = "$Revision: 1.9 $";
+
+#define MsgHead(ptr, cref, mty, dis) \
+	*ptr++ = dis; \
+	*ptr++ = 0x1; \
+	*ptr++ = cref; \
+	*ptr++ = mty
+
+static void
+l3_1TR6_message(struct PStack *st, byte mt, byte pd)
+{
+	struct BufHeader *dibh;
+	byte *p;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, mt, pd);
+
+	dibh->datasize = p - DATAPTR(dibh);
+	st->l3.l3l2(st, DL_DATA, dibh);
+}
+
+static void
+l3_1tr6_setup_req(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+	char *teln;
+
+	st->l3.callref = st->pa->callref;
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, MT_N1_SETUP, PROTO_DIS_N1);
+
+	if ('S' == (st->pa->setup.phone[0] & 0x5f)) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = st->pa->setup.si1;	/* 0 for all Services */
+		*p++ = st->pa->setup.si2;	/* 0 for all Services */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV (default) */
+		*p++ = st->pa->setup.si1;	/* 0 for all Services */
+		*p++ = st->pa->setup.si2;	/* 0 for all Services */
+	}
+	if (st->pa->setup.eazmsn[0] != '\0') {
+		*p++ = WE0_origAddr;
+		*p++ = strlen(st->pa->setup.eazmsn) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		teln = st->pa->setup.eazmsn;
+		while (*teln)
+			*p++ = *teln++ & 0x7f;
+	}
+	*p++ = WE0_destAddr;
+	teln = st->pa->setup.phone;
+	if ('S' != (st->pa->setup.phone[0] & 0x5f)) {	/* Keine SPV */
+		*p++ = strlen(st->pa->setup.phone) + 1;
+		st->pa->spv = 0;
+	} else {		/* SPV */
+		*p++ = strlen(st->pa->setup.phone);
+		teln++;		/* skip S */
+		st->pa->spv = 1;
+	}
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+	*p++ = WE_Shift_F6;
+	/* Codesatz 6 fuer Service */
+	*p++ = WE6_serviceInd;
+	*p++ = 2;		/* len=2 info,info2 */
+	*p++ = st->pa->setup.si1;
+	*p++ = st->pa->setup.si2;
+
+	dibh->datasize = p - DATAPTR(dibh);
+
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303);
+	newl3state(st, 1);
+	st->l3.l3l2(st, DL_DATA, dibh);
+
+}
+
+static void
+l3_1tr6_setup(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct BufHeader *ibh = arg;
+
+	p = DATAPTR(ibh);
+	p += st->l2.uihsize;
+	st->pa->callref = getcallref(p);
+	st->l3.callref = 0x80 + st->pa->callref;
+
+	/* Channel Identification */
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+		bcfound++;
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without bchannel");
+
+	p = DATAPTR(ibh);
+
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, WE6_serviceInd, 6))) {
+		st->pa->setup.si1 = p[2];
+		st->pa->setup.si2 = p[3];
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without service indicator");
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			WE0_destAddr, 0)))
+		iecpy(st->pa->setup.eazmsn, p, 1);
+	else
+		st->pa->setup.eazmsn[0] = 0;
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			WE0_origAddr, 0))) {
+		iecpy(st->pa->setup.phone, p, 1);
+	} else
+		st->pa->setup.phone[0] = 0;
+
+	p = DATAPTR(ibh);
+	st->pa->spv = 0;
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			WE0_netSpecFac, 0))) {
+		if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
+			st->pa->spv = 1;
+	}
+	BufPoolRelease(ibh);
+
+	/* Signal all services, linklevel takes care of Service-Indicator */
+	if (bcfound) {
+		if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) {
+			sprintf(tmp, "non-digital call: %s -> %s",
+				st->pa->setup.phone,
+				st->pa->setup.eazmsn);
+			l3_debug(st, tmp);
+		}
+		newl3state(st, 6);
+		st->l3.l3l4(st, CC_SETUP_IND, NULL);
+	}
+}
+
+static void
+l3_1tr6_setup_ack(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	struct BufHeader *ibh = arg;
+
+	L3DelTimer(&st->l3.timer);
+	newl3state(st, 2);
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup answer without bchannel");
+
+	BufPoolRelease(ibh);
+	L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304);
+	st->l3.l3l4(st, CC_MORE_INFO, NULL);
+}
+
+static void
+l3_1tr6_call_sent(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	struct BufHeader *ibh = arg;
+
+	L3DelTimer(&st->l3.timer);
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE0_chanID, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup answer without bchannel");
+
+	BufPoolRelease(ibh);
+	L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310);
+	newl3state(st, 3);
+	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3_1tr6_alert(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	L3DelTimer(&st->l3.timer);	/* T304 */
+	newl3state(st, 4);
+	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3_1tr6_info(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+	struct BufHeader *ibh = arg;
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > st->pa->chargeinfo) {
+			st->pa->chargeinfo = tmpcharge;
+			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		}
+		if (st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", st->pa->chargeinfo);
+			l3_debug(st, tmp);
+		}
+	} else if (st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(st, "charging info not found");
+
+	BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_info_s2(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+}
+
+static void
+l3_1tr6_connect(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	L3DelTimer(&st->l3.timer);	/* T310 */
+	newl3state(st, 10);
+	st->pa->chargeinfo = 0;
+	BufPoolRelease(ibh);
+	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+}
+
+static void
+l3_1tr6_rel(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+	byte *p;
+	
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE0_cause, 0))) {
+		if (p[1] > 0) {
+			st->pa->cause = p[2];
+			if (p[1] > 1)
+				st->pa->loc = p[3];
+			else
+				st->pa->loc = 0;
+		} else {
+			st->pa->cause = 0;
+			st->pa->loc = 0;
+		}
+	} else 
+		st->pa->cause = -1;
+	BufPoolRelease(ibh);
+	StopAllL3Timer(st);
+	newl3state(st, 0);
+	l3_1TR6_message(st, MT_N1_REL_ACK, PROTO_DIS_N1);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3_1tr6_rel_ack(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	StopAllL3Timer(st);
+	newl3state(st, 0);
+	st->pa->cause = -1;
+	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3_1tr6_disc(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+	byte *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+
+	StopAllL3Timer(st);
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > st->pa->chargeinfo) {
+			st->pa->chargeinfo = tmpcharge;
+			st->l3.l3l4(st, CC_INFO_CHARGE, NULL);
+		}
+		if (st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", st->pa->chargeinfo);
+			l3_debug(st, tmp);
+		}
+	} else if (st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(st, "charging info not found");
+
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			WE0_cause, 0))) {
+		if (p[1] > 0) {
+			st->pa->cause = p[2];
+			if (p[1] > 1)
+				st->pa->loc = p[3];
+			else
+				st->pa->loc = 0;
+		} else {
+			st->pa->cause = 0;
+			st->pa->loc = 0;
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "cause not found");
+		st->pa->cause = -1;
+	}
+	BufPoolRelease(ibh);
+	newl3state(st, 12);
+	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+
+static void
+l3_1tr6_connect_ack(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	newl3state(st, 10);
+	st->pa->chargeinfo = 0;
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+}
+
+static void
+l3_1tr6_alert_req(struct PStack *st, byte pr,
+		  void *arg)
+{
+	newl3state(st, 7);
+	l3_1TR6_message(st, MT_N1_ALERT, PROTO_DIS_N1);
+}
+
+static void
+l3_1tr6_setup_rsp(struct PStack *st, byte pr,
+		  void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+	MsgHead(p, st->l3.callref, MT_N1_CONN, PROTO_DIS_N1);
+	if (st->pa->spv) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = st->pa->setup.si1;
+		*p++ = st->pa->setup.si2;
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV */
+		*p++ = st->pa->setup.si1;
+		*p++ = st->pa->setup.si2;
+	}
+	newl3state(st, 8);
+	dibh->datasize = p - DATAPTR(dibh);
+	st->l3.l3l2(st, DL_DATA, dibh);
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313);
+}
+
+static void
+l3_1tr6_reset(struct PStack *st, byte pr, void *arg)
+{
+	newl3state(st, 0);
+}
+
+static void
+l3_1tr6_disconnect_req(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+	byte cause = 0x10;
+	byte clen = 1;
+
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+	case 0x10:
+		clen = 0;
+		break;
+	case 0x15:
+		cause = CAUSE_CallRejected;
+		break;
+	}
+	StopAllL3Timer(st);
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 21);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+	MsgHead(p, st->l3.callref, MT_N1_DISC, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause | 0x80;
+	dibh->datasize = p - DATAPTR(dibh);
+	newl3state(st, 11);
+	st->l3.l3l2(st, DL_DATA, dibh);
+	L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305);
+}
+
+static void
+l3_1tr6_release_req(struct PStack *st, byte 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, byte pr, void *arg)
+{
+	if (st->l3.n_t303 > 0) {
+		st->l3.n_t303--;
+		L3DelTimer(&st->l3.timer);
+		l3_1tr6_setup_req(st, pr, arg);
+	} else {
+		L3DelTimer(&st->l3.timer);
+		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
+		st->l3.n_t303 = 1;
+		newl3state(st, 0);
+	}
+}
+
+static void
+l3_1tr6_t304(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3_1tr6_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+
+}
+
+static void
+l3_1tr6_t305(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+	byte cause = 0x90;
+	byte clen = 1;
+
+	L3DelTimer(&st->l3.timer);
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+	case 0x10:
+		clen = 0;
+		break;
+	case 0x15:
+		cause = CAUSE_CallRejected;
+		break;
+	}
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+	MsgHead(p, st->l3.callref, MT_N1_REL, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause;
+	dibh->datasize = p - DATAPTR(dibh);
+	newl3state(st, 19);
+	st->l3.l3l2(st, DL_DATA, dibh);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+}
+
+static void
+l3_1tr6_t310(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3_1tr6_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+}
+
+static void
+l3_1tr6_t313(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3_1tr6_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_CONNECT_ERR, NULL);
+}
+
+static void
+l3_1tr6_t308_1(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	l3_1TR6_message(st, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2);
+	newl3state(st, 19);
+}
+
+static void
+l3_1tr6_t308_2(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
+	newl3state(st, 0);
+}
+
+static struct stateentry downstl[] =
+{
+	{SBIT(0),
+	 CC_SETUP_REQ, l3_1tr6_setup_req},
+   	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) |
+    	 SBIT(10),
+    	 CC_DISCONNECT_REQ, l3_1tr6_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE_REQ, l3_1tr6_release_req},
+	{ALL_STATES,
+	 CC_DLRL, l3_1tr6_reset},
+	{SBIT(6),
+	 CC_IGNORE, l3_1tr6_reset},
+	{SBIT(6),
+	 CC_REJECT_REQ, l3_1tr6_disconnect_req},
+	{SBIT(6),
+	 CC_ALERTING_REQ, l3_1tr6_alert_req},
+	{SBIT(6) | SBIT(7),
+	 CC_SETUP_RSP, l3_1tr6_setup_rsp},
+	{SBIT(1),
+	 CC_T303, l3_1tr6_t303},
+	{SBIT(2),
+	 CC_T304, l3_1tr6_t304},
+	{SBIT(3),
+	 CC_T310, l3_1tr6_t310},
+	{SBIT(8),
+	 CC_T313, l3_1tr6_t313},
+	{SBIT(11),
+	 CC_T305, l3_1tr6_t305},
+	{SBIT(19),
+	 CC_T308_1, l3_1tr6_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3_1tr6_t308_2},
+};
+
+static int downstl_len = sizeof(downstl) /
+sizeof(struct stateentry);
+
+static struct stateentry datastln1[] =
+{
+	{SBIT(0),
+	 MT_N1_SETUP, l3_1tr6_setup},
+	{SBIT(1),
+	 MT_N1_SETUP_ACK, l3_1tr6_setup_ack},
+	{SBIT(1) | SBIT(2),
+	 MT_N1_CALL_SENT, l3_1tr6_call_sent},
+   {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+    MT_N1_DISC, l3_1tr6_disc},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_ALERT, l3_1tr6_alert},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_CONN, l3_1tr6_connect},
+	{SBIT(2),
+	 MT_N1_INFO, l3_1tr6_info_s2},
+	{SBIT(8),
+	 MT_N1_CONN_ACK, l3_1tr6_connect_ack},
+	{SBIT(10),
+	 MT_N1_INFO, l3_1tr6_info},
+   {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+    SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+    MT_N1_REL, l3_1tr6_rel},
+	{SBIT(19),
+	 MT_N1_REL_ACK, l3_1tr6_rel_ack}
+};
+
+
+static int datastln1_len = sizeof(datastln1) /
+sizeof(struct stateentry);
+
+static void
+up1tr6(struct PStack *st,
+       int pr, void *arg)
+{
+	int i, mt, size;
+	byte *ptr;
+	struct BufHeader *ibh = arg;
+	char tmp[80];
+
+	if (pr == DL_DATA) {
+		ptr = DATAPTR(ibh);
+		ptr += st->l2.ihsize;
+		size = ibh->datasize - st->l2.ihsize;
+	} else if (pr == DL_UNIT_DATA) {
+		ptr = DATAPTR(ibh);
+		ptr += st->l2.uihsize;
+		size = ibh->datasize - st->l2.uihsize;
+	} else {
+		if (st->l3.debug & L3_DEB_WARN) {
+			sprintf(tmp, "up1tr6 unknown data typ %d state %d",
+				pr, st->l3.state);
+			l3_debug(st, tmp);
+		}
+		BufPoolRelease(ibh);
+		return;
+	}
+	if ((ptr[0] & 0xfe) != PROTO_DIS_N0) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d state %d",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				ptr[0], size, st->l3.state);
+			l3_debug(st, tmp);
+		}
+		BufPoolRelease(ibh);
+		return;
+	}
+	mt = ptr[3];
+
+	if (ptr[0] == PROTO_DIS_N0) {
+		BufPoolRelease(ibh);
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "up1tr6%s N0 state %d mt %x unhandled",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				st->l3.state, mt);
+			l3_debug(st, tmp);
+		}
+	} else if (ptr[0] == PROTO_DIS_N1) {
+		for (i = 0; i < datastln1_len; i++)
+			if ((mt == datastln1[i].primitive) &&
+			    ((1 << st->l3.state) & datastln1[i].state))
+				break;
+		if (i == datastln1_len) {
+			BufPoolRelease(ibh);
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
+				  (pr == DL_DATA) ? " " : "(broadcast) ",
+					st->l3.state, mt);
+				l3_debug(st, tmp);
+			}
+			return;
+		} else {
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x",
+				  (pr == DL_DATA) ? " " : "(broadcast) ",
+					st->l3.state, mt);
+				l3_debug(st, tmp);
+			}
+			datastln1[i].rout(st, pr, ibh);
+		}
+	}
+}
+
+static void
+down1tr6(struct PStack *st,
+	 int pr, void *arg)
+{
+	int i;
+	struct BufHeader *ibh = arg;
+	char tmp[80];
+
+	for (i = 0; i < downstl_len; i++)
+		if ((pr == downstl[i].primitive) &&
+		    ((1 << st->l3.state) & downstl[i].state))
+			break;
+	if (i == downstl_len) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+		downstl[i].rout(st, pr, ibh);
+	}
+}
+
+void
+setstack_1tr6(struct PStack *st)
+{
+	char tmp[64];
+
+	st->l4.l4l3 = down1tr6;
+	st->l2.l2l3 = up1tr6;
+	st->l3.t303 = 4000;
+	st->l3.t304 = 20000;
+	st->l3.t305 = 4000;
+	st->l3.t308 = 4000;
+	st->l3.t310 = 120000;
+	st->l3.t313 = 4000;
+	st->l3.t318 = 4000;
+	st->l3.t319 = 4000;
+	st->l3.n_t303 = 0;
+
+	if (st->l3.channr & 1) {
+		strcpy(tmp, l3_1tr6_revision);
+		printk(KERN_NOTICE "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
+	}
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/l3_1tr6.h linux/drivers/isdn/hisax/l3_1tr6.h
--- v2.1.26/linux/drivers/isdn/hisax/l3_1tr6.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3_1tr6.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,160 @@
+/* $Id: l3_1tr6.h,v 1.1 1996/10/13 20:03:48 keil Exp $
+ *
+ *  German 1TR6 D-channel protocol defines
+ *
+ * $Log: l3_1tr6.h,v $
+ * Revision 1.1  1996/10/13 20:03:48  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#ifndef l3_1tr6
+#define l3_1tr6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 0x61
+#define MT_N0_CANC_IND 0x62
+#define MT_N0_FAC_STA 0x63
+#define MT_N0_STA_ACK 0x64
+#define MT_N0_STA_REJ 0x65
+#define MT_N0_FAC_INF 0x66
+#define MT_N0_INF_ACK 0x67
+#define MT_N0_INF_REJ 0x68
+#define MT_N0_CLOSE   0x75
+#define MT_N0_CLO_ACK 0x77
+
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+
+
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SPV 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAccessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/l3dss1.c linux/drivers/isdn/hisax/l3dss1.c
--- v2.1.26/linux/drivers/isdn/hisax/l3dss1.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/l3dss1.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,771 @@
+/* $Id: l3dss1.c,v 1.12 1997/02/17 00:34:26 keil Exp $
+
+ * EURO/DSS1 D-channel protocol
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: l3dss1.c,v $
+ * Revision 1.12  1997/02/17 00:34:26  keil
+ * Bugfix: Wrong cause delivered
+ *
+ * Revision 1.11  1997/02/16 12:12:47  fritz
+ * Bugfix: SI2 was nont initialized on incoming calls.
+ *
+ * Revision 1.10  1997/02/11 01:37:24  keil
+ * Changed setup-interface (incoming and outgoing)
+ *
+ * Revision 1.9  1997/01/27 23:20:52  keil
+ * report revision only ones
+ *
+ * Revision 1.8  1997/01/21 22:29:41  keil
+ * new statemachine; L3 timers
+ *
+ * Revision 1.7  1996/12/14 21:06:59  keil
+ * additional states for CC_REJECT
+ *
+ * Revision 1.6  1996/12/08 22:59:16  keil
+ * fixed calling party number without octet 3a
+ *
+ * Revision 1.5  1996/12/08 19:53:31  keil
+ * fixes from Pekka Sarnila
+ *
+ * Revision 1.4  1996/11/05 19:44:36  keil
+ * some fixes from Henner Eisen
+ *
+ * Revision 1.3  1996/10/30 10:18:01  keil
+ * bugfixes in debugging output
+ *
+ * Revision 1.2  1996/10/27 22:15:16  keil
+ * bugfix reject handling
+ *
+ * Revision 1.1  1996/10/13 20:04:55  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isdnl3.h"
+
+extern char *HiSax_getrev(const char *revision);
+const char *dss1_revision = "$Revision: 1.12 $";
+
+#define	MsgHead(ptr, cref, mty) \
+	*ptr++ = 0x8; \
+	*ptr++ = 0x1; \
+	*ptr++ = cref; \
+	*ptr++ = mty
+
+static void
+l3dss1_message(struct PStack *st, byte mt)
+{
+	struct BufHeader *dibh;
+	byte *p;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 18);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, mt);
+
+	dibh->datasize = p - DATAPTR(dibh);
+	st->l3.l3l2(st, DL_DATA, dibh);
+}
+
+static void
+l3dss1_release_req(struct PStack *st, byte pr, void *arg)
+{
+	StopAllL3Timer(st);
+	newl3state(st, 19);
+	l3dss1_message(st, MT_RELEASE);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+}
+
+static void
+l3dss1_release_cmpl(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	struct BufHeader *ibh = arg;
+	int cause = -1;
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2) 
+			st->pa->loc = *p++;
+		cause = *p & 0x7f;
+	}
+	BufPoolRelease(ibh);
+	StopAllL3Timer(st);
+	st->pa->cause = cause;
+	newl3state(st, 0);
+	st->l3.l3l4(st, CC_RELEASE_CNF, NULL);
+}
+
+static void
+l3dss1_setup_req(struct PStack *st, byte pr,
+		 void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+	char *teln;
+
+	st->l3.callref = st->pa->callref;
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 19);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, MT_SETUP);
+
+	/*
+	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+	 */
+	*p++ = 0xa1;
+	switch (st->pa->setup.si1) {
+	case 1:		/* Telephony                               */
+		*p++ = 0x4;	/* BC-IE-code                              */
+		*p++ = 0x3;	/* Length                                  */
+		*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio     */
+		*p++ = 0x90;	/* Circuit-Mode 64kbps                     */
+		*p++ = 0xa3;	/* A-Law Audio                             */
+		break;
+	case 5:		/* Datatransmission 64k, BTX               */
+	case 7:		/* Datatransmission 64k                    */
+	default:
+		*p++ = 0x4;	/* BC-IE-code                              */
+		*p++ = 0x2;	/* Length                                  */
+		*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+		*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+		break;
+	}
+	/*
+	 * What about info2? Mapping to High-Layer-Compatibility?
+	 */
+	if (st->pa->setup.eazmsn[0]) {
+		*p++ = 0x6c;
+		*p++ = strlen(st->pa->setup.eazmsn) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		teln = st->pa->setup.eazmsn;
+		while (*teln)
+			*p++ = *teln++ & 0x7f;
+	}
+	*p++ = 0x70;
+	*p++ = strlen(st->pa->setup.phone) + 1;
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+
+	teln = st->pa->setup.phone;
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+
+	dibh->datasize = p - DATAPTR(dibh);
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t303, CC_T303);
+	newl3state(st, 1);
+	st->l3.l3l2(st, DL_DATA, dibh);
+
+}
+
+static void
+l3dss1_call_proc(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	struct BufHeader *ibh = arg;
+
+	L3DelTimer(&st->l3.timer);
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			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");
+	BufPoolRelease(ibh);
+	newl3state(st, 3);
+	L3AddTimer(&st->l3.timer, st->l3.t310, CC_T310);
+	st->l3.l3l4(st, CC_PROCEEDING_IND, NULL);
+}
+
+static void
+l3dss1_setup_ack(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	struct BufHeader *ibh = arg;
+
+	L3DelTimer(&st->l3.timer);
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			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");
+
+	BufPoolRelease(ibh);
+	newl3state(st, 2);
+	L3AddTimer(&st->l3.timer, st->l3.t304, CC_T304);
+	st->l3.l3l4(st, CC_MORE_INFO, NULL);
+}
+
+static void
+l3dss1_disconnect(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	struct BufHeader *ibh = arg;
+	int cause = -1;
+
+	StopAllL3Timer(st);
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			st->pa->loc = *p++;
+		cause = *p & 0x7f;
+	}
+	BufPoolRelease(ibh);
+	newl3state(st, 12);
+	st->pa->cause = cause;
+	st->l3.l3l4(st, CC_DISCONNECT_IND, NULL);
+}
+
+
+static void
+l3dss1_connect(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+
+	BufPoolRelease(ibh);
+	L3DelTimer(&st->l3.timer);	/* T310 */
+	newl3state(st, 10);
+	st->l3.l3l4(st, CC_SETUP_CNF, NULL);
+}
+
+static void
+l3dss1_alerting(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	L3DelTimer(&st->l3.timer);	/* T304 */
+	newl3state(st, 4);
+	st->l3.l3l4(st, CC_ALERTING_IND, NULL);
+}
+
+static void
+l3dss1_setup(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct BufHeader *ibh = arg;
+
+	p = DATAPTR(ibh);
+	p += st->l2.uihsize;
+	st->pa->callref = getcallref(p);
+	st->l3.callref = 0x80 + st->pa->callref;
+
+	/*
+	 * Channel Identification
+	 */
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			0x18, 0))) {
+		st->pa->bchannel = p[2] & 0x3;
+		if (st->pa->bchannel)
+			bcfound++;
+		else if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "setup without bchannel");
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without bchannel");
+
+	p = DATAPTR(ibh);
+	/*
+	   * Bearer Capabilities
+	 */
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x04, 0))) {
+		st->pa->setup.si2 = 0;
+		switch (p[2] & 0x1f) {
+		case 0x00:
+			/* Speech */
+		case 0x10:
+			/* 3.1 Khz audio */
+			st->pa->setup.si1 = 1;
+			break;
+		case 0x08:
+			/* Unrestricted digital information */
+			st->pa->setup.si1 = 7;
+			break;
+		case 0x09:
+			/* Restricted digital information */
+			st->pa->setup.si1 = 2;
+			break;
+		case 0x11:
+			/* Unrestr. digital information  with tones/announcements */
+			st->pa->setup.si1 = 3;
+			break;
+		case 0x18:
+			/* Video */
+			st->pa->setup.si1 = 4;
+			break;
+		default:
+			st->pa->setup.si1 = 0;
+		}
+	} else if (st->l3.debug & L3_DEB_WARN)
+		l3_debug(st, "setup without bearer capabilities");
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			0x70, 0)))
+		iecpy(st->pa->setup.eazmsn, p, 1);
+	else
+		st->pa->setup.eazmsn[0] = 0;
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
+			0x6c, 0))) {
+		st->pa->setup.plan = p[2];
+		if (p[2] & 0x80) {
+			iecpy(st->pa->setup.phone, p, 1);
+			st->pa->setup.screen = 0;
+		} else {
+			iecpy(st->pa->setup.phone, p, 2);
+			st->pa->setup.screen = p[3];
+		}
+	} else {
+		st->pa->setup.phone[0] = 0;
+		st->pa->setup.plan = 0;
+		st->pa->setup.screen = 0;
+	}
+	BufPoolRelease(ibh);
+
+	if (bcfound) {
+		if ((st->pa->setup.si1 != 7) && (st->l3.debug & L3_DEB_WARN)) {
+			sprintf(tmp, "non-digital call: %s -> %s",
+				st->pa->setup.phone,
+				st->pa->setup.eazmsn);
+			l3_debug(st, tmp);
+		}
+		newl3state(st, 6);
+		st->l3.l3l4(st, CC_SETUP_IND, NULL);
+	}
+}
+
+static void
+l3dss1_reset(struct PStack *st, byte pr, void *arg)
+{
+	StopAllL3Timer(st);
+	newl3state(st, 0);
+}
+
+static void
+l3dss1_setup_rsp(struct PStack *st, byte pr,
+		 void *arg)
+{
+	newl3state(st, 8);
+	l3dss1_message(st, MT_CONNECT);
+	L3DelTimer(&st->l3.timer);
+	L3AddTimer(&st->l3.timer, st->l3.t313, CC_T313);
+}
+
+static void
+l3dss1_connect_ack(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *ibh = arg;
+
+	BufPoolRelease(ibh);
+	newl3state(st, 10);
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_SETUP_COMPLETE_IND, NULL);
+}
+
+static void
+l3dss1_disconnect_req(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+	byte cause = 0x10;
+
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+
+	StopAllL3Timer(st);
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, MT_DISCONNECT);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	dibh->datasize = p - DATAPTR(dibh);
+	newl3state(st, 11);
+	st->l3.l3l2(st, DL_DATA, dibh);
+	L3AddTimer(&st->l3.timer, st->l3.t305, CC_T305);
+}
+
+static void
+l3dss1_reject_req(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+	byte cause = 0x95;
+
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, MT_RELEASE_COMPLETE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause;
+
+	dibh->datasize = p - DATAPTR(dibh);
+	newl3state(st, 0);
+	st->l3.l3l2(st, DL_DATA, dibh);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3dss1_release(struct PStack *st, byte pr, void *arg)
+{
+	byte *p;
+	struct BufHeader *ibh = arg;
+	int cause = -1;
+
+	p = DATAPTR(ibh);
+	if ((p = findie(p + st->l2.ihsize, ibh->datasize - st->l2.ihsize,
+			IE_CAUSE, 0))) {
+		p++;
+		if (*p++ == 2)
+			st->pa->loc = *p++;
+		cause = *p & 0x7f;
+	}
+	BufPoolRelease(ibh);
+	StopAllL3Timer(st);
+	st->pa->cause = cause;
+	newl3state(st, 0);
+	l3dss1_message(st, MT_RELEASE_COMPLETE);
+	st->l3.l3l4(st, CC_RELEASE_IND, NULL);
+}
+
+static void
+l3dss1_alert_req(struct PStack *st, byte pr,
+		 void *arg)
+{
+	newl3state(st, 7);
+	l3dss1_message(st, MT_ALERTING);
+}
+
+static void
+l3dss1_status_enq(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh = arg;
+	byte *p;
+
+	BufPoolRelease(dibh);
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 22);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = 0x9E;		/* answer status enquire */
+
+	*p++ = 0x14;		/* CallState */
+	*p++ = 0x1;
+	*p++ = st->l3.state & 0x3f;
+
+	dibh->datasize = p - DATAPTR(dibh);
+	st->l3.l3l2(st, DL_DATA, dibh);
+}
+
+static void
+l3dss1_t303(struct PStack *st, byte pr, void *arg)
+{
+	if (st->l3.n_t303 > 0) {
+		st->l3.n_t303--;
+		L3DelTimer(&st->l3.timer);
+		l3dss1_setup_req(st, pr, arg);
+	} else {
+		newl3state(st, 0);
+		L3DelTimer(&st->l3.timer);
+		st->l3.l3l4(st, CC_NOSETUP_RSP_ERR, NULL);
+		st->l3.n_t303 = 1;
+	}
+}
+
+static void
+l3dss1_t304(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3dss1_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+
+}
+
+static void
+l3dss1_t305(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh;
+	byte *p;
+	byte cause = 0x90;
+
+	L3DelTimer(&st->l3.timer);
+	if (st->pa->cause > 0)
+		cause = st->pa->cause;
+
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+
+	MsgHead(p, st->l3.callref, MT_RELEASE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause;
+
+	dibh->datasize = p - DATAPTR(dibh);
+	newl3state(st, 19);
+	st->l3.l3l2(st, DL_DATA, dibh);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_1);
+}
+
+static void
+l3dss1_t310(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3dss1_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_SETUP_ERR, NULL);
+}
+
+static void
+l3dss1_t313(struct PStack *st, byte pr, void *arg)
+{
+	L3DelTimer(&st->l3.timer);
+	st->pa->cause = 0xE6;
+	l3dss1_disconnect_req(st, pr, NULL);
+	st->l3.l3l4(st, CC_CONNECT_ERR, NULL);
+}
+
+static void
+l3dss1_t308_1(struct PStack *st, byte pr, void *arg)
+{
+	newl3state(st, 19);
+	L3DelTimer(&st->l3.timer);
+	l3dss1_message(st, MT_RELEASE);
+	L3AddTimer(&st->l3.timer, st->l3.t308, CC_T308_2);
+}
+
+static void
+l3dss1_t308_2(struct PStack *st, byte pr, void *arg)
+{
+	newl3state(st, 0);
+	L3DelTimer(&st->l3.timer);
+	st->l3.l3l4(st, CC_RELEASE_ERR, NULL);
+}
+
+static struct stateentry downstatelist[] =
+{
+	{SBIT(0),
+	 CC_SETUP_REQ, l3dss1_setup_req},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10),
+	 CC_DISCONNECT_REQ, l3dss1_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE_REQ, l3dss1_release_req},
+	{ALL_STATES,
+	 CC_DLRL, l3dss1_reset},
+	{SBIT(6),
+	 CC_IGNORE, l3dss1_reset},
+	{SBIT(6),
+	 CC_REJECT_REQ, l3dss1_reject_req},
+	{SBIT(6),
+	 CC_ALERTING_REQ, l3dss1_alert_req},
+	{SBIT(6) | SBIT(7),
+	 CC_SETUP_RSP, l3dss1_setup_rsp},
+	{SBIT(1),
+	 CC_T303, l3dss1_t303},
+	{SBIT(2),
+	 CC_T304, l3dss1_t304},
+	{SBIT(3),
+	 CC_T310, l3dss1_t310},
+	{SBIT(8),
+	 CC_T313, l3dss1_t313},
+	{SBIT(11),
+	 CC_T305, l3dss1_t305},
+	{SBIT(19),
+	 CC_T308_1, l3dss1_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3dss1_t308_2},
+};
+
+static int downsllen = sizeof(downstatelist) /
+sizeof(struct stateentry);
+
+static struct stateentry datastatelist[] =
+{
+	{ALL_STATES,
+	 MT_STATUS_ENQUIRY, l3dss1_status_enq},
+	{SBIT(0) | SBIT(6),
+	 MT_SETUP, l3dss1_setup},
+	{SBIT(1) | SBIT(2),
+	 MT_CALL_PROCEEDING, l3dss1_call_proc},
+	{SBIT(1),
+	 MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack},
+	{SBIT(1) | SBIT(2) | SBIT(3),
+	 MT_ALERTING, l3dss1_alerting},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+	 MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+	 MT_RELEASE, l3dss1_release},
+   {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+    MT_DISCONNECT, l3dss1_disconnect},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_CONNECT, l3dss1_connect},
+	{SBIT(8),
+	 MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
+};
+
+static int datasllen = sizeof(datastatelist) /
+sizeof(struct stateentry);
+
+static void
+dss1up(struct PStack *st,
+       int pr, void *arg)
+{
+	int i, mt, size;
+	byte *ptr;
+	struct BufHeader *ibh = arg;
+	char tmp[80];
+
+	if (pr == DL_DATA) {
+		ptr = DATAPTR(ibh);
+		ptr += st->l2.ihsize;
+		size = ibh->datasize - st->l2.ihsize;
+	} else if (pr == DL_UNIT_DATA) {
+		ptr = DATAPTR(ibh);
+		ptr += st->l2.uihsize;
+		size = ibh->datasize - st->l2.uihsize;
+	} else {
+		if (st->l3.debug & L3_DEB_WARN) {
+			sprintf(tmp, "dss1up unknown data typ %d state %d",
+				pr, st->l3.state);
+			l3_debug(st, tmp);
+		}
+		BufPoolRelease(ibh);
+		return;
+	}
+	if (ptr[0] != PROTO_DIS_EURO) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d state %d",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				ptr[0], size, st->l3.state);
+			l3_debug(st, tmp);
+		}
+		BufPoolRelease(ibh);
+		return;
+	}
+	mt = ptr[3];
+	for (i = 0; i < datasllen; i++)
+		if ((mt == datastatelist[i].primitive) &&
+		    ((1 << st->l3.state) & datastatelist[i].state))
+			break;
+	if (i == datasllen) {
+		BufPoolRelease(ibh);
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1up%sstate %d mt %x unhandled",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				st->l3.state, mt);
+			l3_debug(st, tmp);
+		}
+		return;
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1up%sstate %d mt %x",
+				(pr == DL_DATA) ? " " : "(broadcast) ",
+				st->l3.state, mt);
+			l3_debug(st, tmp);
+		}
+		datastatelist[i].rout(st, pr, ibh);
+	}
+}
+
+static void
+dss1down(struct PStack *st,
+	 int pr, void *arg)
+{
+	int i;
+	struct BufHeader *ibh = arg;
+	char tmp[80];
+
+	for (i = 0; i < downsllen; i++)
+		if ((pr == downstatelist[i].primitive) &&
+		    ((1 << st->l3.state) & downstatelist[i].state))
+			break;
+	if (i == downsllen) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1down state %d prim %d unhandled",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "dss1down state %d prim %d",
+				st->l3.state, pr);
+			l3_debug(st, tmp);
+		}
+		downstatelist[i].rout(st, pr, ibh);
+	}
+}
+
+void
+setstack_dss1(struct PStack *st)
+{
+	char tmp[64];
+
+	st->l4.l4l3 = dss1down;
+	st->l2.l2l3 = dss1up;
+	st->l3.t303 = 4000;
+	st->l3.t304 = 30000;
+	st->l3.t305 = 30000;
+	st->l3.t308 = 4000;
+	st->l3.t310 = 30000;
+	st->l3.t313 = 4000;
+	st->l3.t318 = 4000;
+	st->l3.t319 = 4000;
+	st->l3.n_t303 = 1;
+
+	if (st->l3.channr & 1) {
+		strcpy(tmp, dss1_revision);
+		printk(KERN_NOTICE "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
+	}
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/q931.c linux/drivers/isdn/hisax/q931.c
--- v2.1.26/linux/drivers/isdn/hisax/q931.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/q931.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,1215 @@
+/* $Id: q931.c,v 1.4 1997/02/09 00:29:11 keil Exp $
+
+ * q931.c       code to decode ITU Q.931 call control messages
+ *
+ * Author       Jan den Ouden
+ *
+ * Changelog
+ *
+ * Pauline Middelink    general improvements
+ *
+ * Beat Doebeli         cause texts, display information element
+ *
+ * Karsten Keil         cause texts, display information element for 1TR6
+ *
+ *
+ * $Log: q931.c,v $
+ * Revision 1.4  1997/02/09 00:29:11  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.3  1997/01/21 22:24:59  keil
+ * cleanups
+ *
+ * Revision 1.2  1996/10/27 22:12:45  keil
+ * reporting unknown level 3 protocol ids
+ *
+ * Revision 1.1  1996/10/13 20:04:56  keil
+ * Initial revision
+ *
+ *
+ */
+
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "l3_1tr6.h"
+
+byte *
+findie(byte * p, int size, byte ie, int wanted_set)
+{
+	int l, codeset, maincodeset;
+	byte *pend = p + size;
+
+	/* skip protocol discriminator, callref and message type */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	p++;
+	codeset = 0;
+	maincodeset = 0;
+	/* while there are bytes left... */
+	while (p < pend) {
+		if ((*p & 0xf0) == 0x90) {
+			codeset = *p & 0x07;
+			if (!(*p & 0x08))
+				maincodeset = codeset;
+		}
+		if (*p & 0x80)
+			p++;
+		else {
+			if (codeset == wanted_set) {
+				if (*p == ie)
+					return (p);
+				if (*p > ie)
+					return (NULL);
+			}
+			p++;
+			l = *p++;
+			p += l;
+			codeset = maincodeset;
+		}
+	}
+	return (NULL);
+}
+
+void
+iecpy(byte * dest, byte * iestart, int ieoffset)
+{
+	byte *p;
+	int l;
+
+	p = iestart + ieoffset + 2;
+	l = iestart[1] - ieoffset;
+	while (l--)
+		*dest++ = *p++;
+	*dest++ = '\0';
+}
+
+int
+getcallref(byte * p)
+{
+	p++;			/* prot discr */
+	p++;			/* callref length */
+	return (*p);		/* assuming one-byte callref */
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+	byte nr;
+	char *descr;
+} mtlist[] = {
+
+	{
+		0x1, "ALERTING"
+	},
+	{
+		0x2, "CALL PROCEEDING"
+	},
+	{
+		0x7, "CONNECT"
+	},
+	{
+		0xf, "CONNECT ACKNOWLEDGE"
+	},
+	{
+		0x3, "PROGRESS"
+	},
+	{
+		0x5, "SETUP"
+	},
+	{
+		0xd, "SETUP ACKNOWLEDGE"
+	},
+	{
+		0x26, "RESUME"
+	},
+	{
+		0x2e, "RESUME ACKNOWLEDGE"
+	},
+	{
+		0x22, "RESUME REJECT"
+	},
+	{
+		0x25, "SUSPEND"
+	},
+	{
+		0x2d, "SUSPEND ACKNOWLEDGE"
+	},
+	{
+		0x21, "SUSPEND REJECT"
+	},
+	{
+		0x20, "USER INFORMATION"
+	},
+	{
+		0x45, "DISCONNECT"
+	},
+	{
+		0x4d, "RELEASE"
+	},
+	{
+		0x5a, "RELEASE COMPLETE"
+	},
+	{
+		0x46, "RESTART"
+	},
+	{
+		0x4e, "RESTART ACKNOWLEDGE"
+	},
+	{
+		0x60, "SEGMENT"
+	},
+	{
+		0x79, "CONGESTION CONTROL"
+	},
+	{
+		0x7b, "INFORMATION"
+	},
+	{
+		0x62, "FACILITY"
+	},
+	{
+		0x6e, "NOTIFY"
+	},
+	{
+		0x7d, "STATUS"
+	},
+	{
+		0x75, "STATUS ENQUIRY"
+	}
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+static
+struct MessageType mt_n0[] =
+{
+	{MT_N0_REG_IND, "REGister INDication"},
+	{MT_N0_CANC_IND, "CANCel INDication"},
+	{MT_N0_FAC_STA, "FACility STAtus"},
+	{MT_N0_STA_ACK, "STAtus ACKnowledge"},
+	{MT_N0_STA_REJ, "STAtus REJect"},
+	{MT_N0_FAC_INF, "FACility INFormation"},
+	{MT_N0_INF_ACK, "INFormation ACKnowledge"},
+	{MT_N0_INF_REJ, "INFormation REJect"},
+	{MT_N0_CLOSE, "CLOSE"},
+	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
+};
+
+int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType));
+
+static
+struct MessageType mt_n1[] =
+{
+	{MT_N1_ESC, "ESCape"},
+	{MT_N1_ALERT, "ALERT"},
+	{MT_N1_CALL_SENT, "CALL SENT"},
+	{MT_N1_CONN, "CONNect"},
+	{MT_N1_CONN_ACK, "CONNect ACKnowledge"},
+	{MT_N1_SETUP, "SETUP"},
+	{MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
+	{MT_N1_RES, "RESume"},
+	{MT_N1_RES_ACK, "RESume ACKnowledge"},
+	{MT_N1_RES_REJ, "RESume REJect"},
+	{MT_N1_SUSP, "SUSPend"},
+	{MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
+	{MT_N1_SUSP_REJ, "SUSPend REJect"},
+	{MT_N1_USER_INFO, "USER INFO"},
+	{MT_N1_DET, "DETach"},
+	{MT_N1_DISC, "DISConnect"},
+	{MT_N1_REL, "RELease"},
+	{MT_N1_REL_ACK, "RELease ACKnowledge"},
+	{MT_N1_CANC_ACK, "CANCel ACKnowledge"},
+	{MT_N1_CANC_REJ, "CANCel REJect"},
+	{MT_N1_CON_CON, "CONgestion CONtrol"},
+	{MT_N1_FAC, "FACility"},
+	{MT_N1_FAC_ACK, "FACility ACKnowledge"},
+	{MT_N1_FAC_CAN, "FACility CANcel"},
+	{MT_N1_FAC_REG, "FACility REGister"},
+	{MT_N1_FAC_REJ, "FACility REJect"},
+	{MT_N1_INFO, "INFOrmation"},
+	{MT_N1_REG_ACK, "REGister ACKnowledge"},
+	{MT_N1_REG_REJ, "REGister REJect"},
+	{MT_N1_STAT, "STATus"}
+};
+
+int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType));
+
+static struct MessageType fac_1tr6[] =
+{
+	{FAC_Sperre, "Sperre"},
+	{FAC_Forward1, "Forward 1"},
+	{FAC_Forward2, "Forward 2"},
+	{FAC_Konferenz, "Konferenz"},
+	{FAC_GrabBchan, "Grab Bchannel"},
+	{FAC_Reactivate, "Reactivate"},
+	{FAC_Konferenz3, "Dreier Konferenz"},
+	{FAC_Dienstwechsel1, "Einseitiger Dienstwechsel"},
+	{FAC_Dienstwechsel2, "Zweiseitiger Dienstwechsel"},
+	{FAC_NummernIdent, "Rufnummer-Identifizierung"},
+	{FAC_GBG, "GBG"},
+	{FAC_DisplayUebergeben, "Display Uebergeben"},
+	{FAC_DisplayUmgeleitet, "Display Umgeleitet"},
+	{FAC_Unterdruecke, "Unterdruecke Rufnummer"},
+	{FAC_Deactivate, "Deactivate"},
+	{FAC_Activate, "Activate"},
+	{FAC_SPV, "SPV"},
+	{FAC_Rueckwechsel, "Rueckwechsel"},
+	{FAC_Umleitung, "Umleitung"}
+};
+int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType));
+
+
+
+static int
+prbits(char *dest, byte b, int start, int len)
+{
+	char *dp = dest;
+
+	b = b << (8 - start);
+	while (len--) {
+		if (b & 0x80)
+			*dp++ = '1';
+		else
+			*dp++ = '0';
+		b = b << 1;
+	}
+	return (dp - dest);
+}
+
+static
+byte *
+skipext(byte * p)
+{
+	while (!(*p++ & 0x80));
+	return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ *         not yet written...
+ */
+
+static
+struct CauseValue {
+	byte nr;
+	char *edescr;
+	char *ddescr;
+} cvlist[] = {
+
+	{
+		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+	},
+	{
+		0x02, "No route to specified transit network", ""
+	},
+	{
+		0x03, "No route to destination", ""
+	},
+	{
+		0x04, "Send special information tone", ""
+	},
+	{
+		0x05, "Misdialled trunk prefix", ""
+	},
+	{
+		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+	},
+	{
+		0x07, "Channel awarded and being delivered in an established channel", ""
+	},
+	{
+		0x08, "Preemption", ""
+	},
+	{
+		0x09, "Preemption - circuit reserved for reuse", ""
+	},
+	{
+		0x10, "Normal call clearing", "Normale Ausloesung"
+	},
+	{
+		0x11, "User busy", "TNB besetzt"
+	},
+	{
+		0x12, "No user responding", ""
+	},
+	{
+		0x13, "No answer from user (user alerted)", ""
+	},
+	{
+		0x14, "Subscriber absent", ""
+	},
+	{
+		0x15, "Call rejected", ""
+	},
+	{
+		0x16, "Number changed", ""
+	},
+	{
+		0x1a, "non-selected user clearing", ""
+	},
+	{
+		0x1b, "Destination out of order", ""
+	},
+	{
+		0x1c, "Invalid number format (address incomplete)", ""
+	},
+	{
+		0x1d, "Facility rejected", ""
+	},
+	{
+		0x1e, "Response to Status enquiry", ""
+	},
+	{
+		0x1f, "Normal, unspecified", ""
+	},
+	{
+		0x22, "No circuit/channel available", ""
+	},
+	{
+		0x26, "Network out of order", ""
+	},
+	{
+		0x27, "Permanent frame mode connection out-of-service", ""
+	},
+	{
+		0x28, "Permanent frame mode connection operational", ""
+	},
+	{
+		0x29, "Temporary failure", ""
+	},
+	{
+		0x2a, "Switching equipment congestion", ""
+	},
+	{
+		0x2b, "Access information discarded", ""
+	},
+	{
+		0x2c, "Requested circuit/channel not available", ""
+	},
+	{
+		0x2e, "Precedence call blocked", ""
+	},
+	{
+		0x2f, "Resource unavailable, unspecified", ""
+	},
+	{
+		0x31, "Quality of service unavailable", ""
+	},
+	{
+		0x32, "Requested facility not subscribed", ""
+	},
+	{
+		0x35, "Outgoing calls barred within CUG", ""
+	},
+	{
+		0x37, "Incoming calls barred within CUG", ""
+	},
+	{
+		0x39, "Bearer capability not authorized", ""
+	},
+	{
+		0x3a, "Bearer capability not presently available", ""
+	},
+	{
+		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+	},
+	{
+		0x3f, "Service or option not available, unspecified", ""
+	},
+	{
+		0x41, "Bearer capability not implemented", ""
+	},
+	{
+		0x42, "Channel type not implemented", ""
+	},
+	{
+		0x43, "Requested facility not implemented", ""
+	},
+	{
+		0x44, "Only restricted digital information bearer capability is available", ""
+	},
+	{
+		0x4f, "Service or option not implemented", ""
+	},
+	{
+		0x51, "Invalid call reference value", ""
+	},
+	{
+		0x52, "Identified channel does not exist", ""
+	},
+	{
+		0x53, "A suspended call exists, but this call identity does not", ""
+	},
+	{
+		0x54, "Call identity in use", ""
+	},
+	{
+		0x55, "No call suspended", ""
+	},
+	{
+		0x56, "Call having the requested call identity has been cleared", ""
+	},
+	{
+		0x57, "User not member of CUG", ""
+	},
+	{
+		0x58, "Incompatible destination", ""
+	},
+	{
+		0x5a, "Non-existent CUG", ""
+	},
+	{
+		0x5b, "Invalid transit network selection", ""
+	},
+	{
+		0x5f, "Invalid message, unspecified", ""
+	},
+	{
+		0x60, "Mandatory information element is missing", ""
+	},
+	{
+		0x61, "Message type non-existent or not implemented", ""
+	},
+	{
+		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+	},
+	{
+		0x63, "Information element/parameter non-existent or not implemented", ""
+	},
+	{
+		0x64, "Invalid information element contents", ""
+	},
+	{
+		0x65, "Message not compatible with call state", ""
+	},
+	{
+		0x66, "Recovery on timer expiry", ""
+	},
+	{
+		0x67, "Parameter non-existent or not implemented - passed on", ""
+	},
+	{
+		0x6e, "Message with unrecognized parameter discarded", ""
+	},
+	{
+		0x6f, "Protocol error, unspecified", ""
+	},
+	{
+		0x7f, "Interworking, unspecified", ""
+	},
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, byte * p)
+{
+	byte *end;
+	char *dp = dest;
+	int i, cause;
+
+	end = p + p[1] + 1;
+	p += 2;
+	dp += sprintf(dp, "    coding ");
+	dp += prbits(dp, *p, 7, 2);
+	dp += sprintf(dp, " location ");
+	dp += prbits(dp, *p, 4, 4);
+	*dp++ = '\n';
+	p = skipext(p);
+
+	cause = 0x7f & *p++;
+
+	/* locate cause value */
+	for (i = 0; i < CVSIZE; i++)
+		if (cvlist[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == CVSIZE)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
+
+	while (!0) {
+		if (p > end)
+			break;
+		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
+		dp += sprintf(dp, " rej %d ", *p & 0x7f);
+		if (*p & 0x80) {
+			*dp++ = '\n';
+			break;
+		} else
+			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+	}
+	return (dp - dest);
+
+}
+
+static
+struct MessageType cause_1tr6[] =
+{
+	{CAUSE_InvCRef, "Invalid Call Reference"},
+	{CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
+	{CAUSE_CIDunknown, "Caller Identity unknown"},
+	{CAUSE_CIDinUse, "Caller Identity in Use"},
+	{CAUSE_NoChans, "No Channels available"},
+	{CAUSE_FacNotImpl, "Facility Not Implemented"},
+	{CAUSE_FacNotSubscr, "Facility Not Subscribed"},
+	{CAUSE_OutgoingBarred, "Outgoing calls barred"},
+	{CAUSE_UserAccessBusy, "User Access Busy"},
+	{CAUSE_NegativeGBG, "Negative GBG"},
+	{CAUSE_UnknownGBG, "Unknown  GBG"},
+	{CAUSE_NoSPVknown, "No SPV known"},
+	{CAUSE_DestNotObtain, "Destination not obtainable"},
+	{CAUSE_NumberChanged, "Number changed"},
+	{CAUSE_OutOfOrder, "Out Of Order"},
+	{CAUSE_NoUserResponse, "No User Response"},
+	{CAUSE_UserBusy, "User Busy"},
+	{CAUSE_IncomingBarred, "Incoming Barred"},
+	{CAUSE_CallRejected, "Call Rejected"},
+	{CAUSE_NetworkCongestion, "Network Congestion"},
+	{CAUSE_RemoteUser, "Remote User initiated"},
+	{CAUSE_LocalProcErr, "Local Procedure Error"},
+	{CAUSE_RemoteProcErr, "Remote Procedure Error"},
+	{CAUSE_RemoteUserSuspend, "Remote User Suspend"},
+	{CAUSE_RemoteUserResumed, "Remote User Resumed"},
+	{CAUSE_UserInfoDiscarded, "User Info Discarded"}
+};
+
+int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
+
+static int
+prcause_1tr6(char *dest, byte * p)
+{
+	char *dp = dest;
+	int i, cause;
+
+	p++;
+	if (0 == *p) {
+		dp += sprintf(dp, "   OK (cause length=0)\n");
+		return (dp - dest);
+	} else if (*p > 1) {
+		dp += sprintf(dp, "    coding ");
+		dp += prbits(dp, p[2], 7, 2);
+		dp += sprintf(dp, " location ");
+		dp += prbits(dp, p[2], 4, 4);
+		*dp++ = '\n';
+	}
+	p++;
+	cause = 0x7f & *p;
+
+	/* locate cause value */
+	for (i = 0; i < cause_1tr6_len; i++)
+		if (cause_1tr6[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == cause_1tr6_len)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cause_1tr6[i].descr);
+
+	return (dp - dest);
+
+}
+
+static int
+prchident(char *dest, byte * p)
+{
+	char *dp = dest;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static int
+prcalled(char *dest, byte * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prcalling(char *dest, byte * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p & 0x80)) {
+		dp += sprintf(dp, "    octet 3a ");
+		dp += prbits(dp, *++p, 8, 8);
+		*dp++ = '\n';
+		l--;
+	};
+	p++;
+
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, byte * p)
+{
+	char *dp = dest, ch;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if ((*p++ & 0x1f) == 0x18) {
+		dp += sprintf(dp, "    octet 4.1 ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 1 */
+	if ((*p & 0x60) == 0x20) {
+		ch = ' ';
+		do {
+			dp += sprintf(dp, "    octet 5%c ", ch);
+			dp += prbits(dp, *p, 8, 8);
+			*dp++ = '\n';
+			if (ch == ' ')
+				ch = 'a';
+			else
+				ch++;
+		}
+		while (!(*p++ & 0x80));
+	}
+	/* check for user information layer 2 */
+	if ((*p & 0x60) == 0x40) {
+		dp += sprintf(dp, "    octet 6  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 3 */
+	if ((*p & 0x60) == 0x60) {
+		dp += sprintf(dp, "    octet 7  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
+static int
+general(char *dest, byte * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static int
+prcharge(char *dest, byte * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    GEA ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "  Anzahl: ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prtext(char *dest, byte * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+display(char *dest, byte * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the * display-information element */
+	dp += sprintf(dp, "   \"");
+	while (l--) {
+		dp += sprintf(dp, "%c", *p++);
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+
+		else
+			ch++;
+	}
+	*dp++ = '\"';
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+int
+prfacility(char *dest, byte * p)
+{
+	char *dp = dest;
+	int l, l2;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "\n");
+	l -= 1;
+
+	while (l > 0) {
+		dp += sprintf(dp, "   octet 4 ");
+		dp += prbits(dp, *p++, 8, 8);
+		dp += sprintf(dp, "\n");
+		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
+		l -= 2;
+		dp += sprintf(dp, "   contents ");
+		while (l2--) {
+			dp += sprintf(dp, "%2x ", *p++);
+			l--;
+		}
+		dp += sprintf(dp, "\n");
+	}
+
+	return (dp - dest);
+}
+
+static
+struct InformationElement {
+	byte nr;
+	char *descr;
+	int (*f) (char *, byte *);
+} ielist[] = {
+
+	{
+		0x00, "Segmented message", general
+	},
+	{
+		0x04, "Bearer capability", prbearer
+	},
+	{
+		0x08, "Cause", prcause
+	},
+	{
+		0x10, "Call identity", general
+	},
+	{
+		0x14, "Call state", general
+	},
+	{
+		0x18, "Channel identification", prchident
+	},
+	{
+		0x1c, "Facility", prfacility
+	},
+	{
+		0x1e, "Progress indicator", general
+	},
+	{
+		0x20, "Network-specific facilities", general
+	},
+	{
+		0x27, "Notification indicator", general
+	},
+	{
+		0x28, "Display", display
+	},
+	{
+		0x29, "Date/Time", general
+	},
+	{
+		0x2c, "Keypad facility", general
+	},
+	{
+		0x34, "Signal", general
+	},
+	{
+		0x40, "Information rate", general
+	},
+	{
+		0x42, "End-to-end delay", general
+	},
+	{
+		0x43, "Transit delay selection and indication", general
+	},
+	{
+		0x44, "Packet layer binary parameters", general
+	},
+	{
+		0x45, "Packet layer window size", general
+	},
+	{
+		0x46, "Packet size", general
+	},
+	{
+		0x47, "Closed user group", general
+	},
+	{
+		0x4a, "Reverse charge indication", general
+	},
+	{
+		0x6c, "Calling party number", prcalling
+	},
+	{
+		0x6d, "Calling party subaddress", general
+	},
+	{
+		0x70, "Called party number", prcalled
+	},
+	{
+		0x71, "Called party subaddress", general
+	},
+	{
+		0x74, "Redirecting number", general
+	},
+	{
+		0x78, "Transit network selection", general
+	},
+	{
+		0x79, "Restart indicator", general
+	},
+	{
+		0x7c, "Low layer compatibility", general
+	},
+	{
+		0x7d, "High layer compatibility", general
+	},
+	{
+		0x7e, "User-user", general
+	},
+	{
+		0x7f, "Escape for extension", general
+	},
+};
+
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+static struct InformationElement we_0[] =
+{
+	{WE0_cause, "Cause", prcause_1tr6},
+	{WE0_connAddr, "Connecting Address", prcalled},
+	{WE0_callID, "Call IDentity", general},
+	{WE0_chanID, "Channel IDentity", general},
+	{WE0_netSpecFac, "Network Specific Facility", general},
+	{WE0_display, "Display", general},
+	{WE0_keypad, "Keypad", general},
+	{WE0_origAddr, "Origination Address", prcalled},
+	{WE0_destAddr, "Destination Address", prcalled},
+	{WE0_userInfo, "User Info", general}
+};
+
+static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement));
+
+static struct InformationElement we_6[] =
+{
+	{WE6_serviceInd, "Service Indicator", general},
+	{WE6_chargingInfo, "Charging Information", prcharge},
+	{WE6_date, "Date", prtext},
+	{WE6_facSelect, "Facility Select", general},
+	{WE6_facStatus, "Facility Status", general},
+	{WE6_statusCalled, "Status Called", general},
+	{WE6_addTransAttr, "Additional Transmission Attributes", general}
+};
+static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement));
+
+int
+QuickHex(char *txt, byte * p, int cnt)
+{
+	register int i;
+	register char *t = txt;
+	register byte w;
+
+	for (i = 0; i < cnt; i++) {
+		*t++ = ' ';
+		w = (p[i] >> 4) & 0x0f;
+		if (w < 10)
+			*t++ = '0' + w;
+		else
+			*t++ = 'A' - 10 + w;
+		w = p[i] & 0x0f;
+		if (w < 10)
+			*t++ = '0' + w;
+		else
+			*t++ = 'A' - 10 + w;
+	}
+	*t++ = 0;
+	return (t - txt);
+}
+
+void
+LogFrame(struct IsdnCardState *sp, byte * buf, int size)
+{
+	char *dp;
+
+	if (size < 1)
+		return;
+	dp = sp->dlogspace;
+	if (size < 4096 / 3 - 10) {
+		dp += sprintf(dp, "HEX:");
+		dp += QuickHex(dp, buf, size);
+		dp--;
+		*dp++ = '\n';
+		*dp = 0;
+	} else
+		sprintf(dp, "LogFrame: warning Frame too big (%d)\n",
+			size);
+	HiSax_putstatus(sp, sp->dlogspace);
+}
+
+void
+dlogframe(struct IsdnCardState *sp, byte * buf, int size, char *comment)
+{
+	byte *bend = buf + size;
+	char *dp;
+	unsigned char pd, cr_l, cr, mt;
+	int i, cs = 0, cs_old = 0, cs_fest = 0;
+
+	if (size < 1)
+		return;
+	/* display header */
+	dp = sp->dlogspace;
+	dp += sprintf(dp, "%s\n", comment);
+
+	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
+		/* locate message type */
+		pd = *buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		if (pd == PROTO_DIS_N0) {	/* N0 */
+			for (i = 0; i < mt_n0_len; i++)
+				if (mt_n0[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == mt_n0_len)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n0[i].descr);
+		} else {	/* N1 */
+			for (i = 0; i < mt_n1_len; i++)
+				if (mt_n1[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == mt_n1_len)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n1[i].descr);
+		}
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						cs_old = cs;
+						cs = *buf & 7;
+						cs_fest = *buf & 8;
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			if (cs == 0) {
+				for (i = 0; i < we_0_len; i++)
+					if (*buf == we_0[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != we_0_len) {
+					dp += sprintf(dp, "  %s\n", we_0[i].descr);
+					dp += we_0[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+			} else if (cs == 6) {
+				for (i = 0; i < we_6_len; i++)
+					if (*buf == we_6[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != we_6_len) {
+					dp += sprintf(dp, "  %s\n", we_6[i].descr);
+					dp += we_6[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+			} else
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+			/* Skip to next element */
+			if (cs_fest == 8) {
+				cs = cs_old;
+				cs_old = 0;
+				cs_fest = 0;
+			}
+			buf += buf[1] + 2;
+		}
+	} else if (buf[0] == 8) {	/* EURO */
+		/* locate message type */
+		buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		for (i = 0; i < MTSIZE; i++)
+			if (mtlist[i].nr == mt)
+				break;
+
+		/* display message type if it exists */
+		if (i == MTSIZE)
+			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mt);
+		else
+			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mtlist[i].descr);
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 5:
+						dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			for (i = 0; i < IESIZE; i++)
+				if (*buf == ielist[i].nr)
+					break;
+
+			/* When not found, give appropriate msg */
+			if (i != IESIZE) {
+				dp += sprintf(dp, "  %s\n", ielist[i].descr);
+				dp += ielist[i].f(dp, buf);
+			} else
+				dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+
+			/* Skip to next element */
+			buf += buf[1] + 2;
+		}
+	} else {
+		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
+	}
+	dp += sprintf(dp, "\n");
+	HiSax_putstatus(sp, sp->dlogspace);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/siemens.h linux/drivers/isdn/hisax/siemens.h
--- v2.1.26/linux/drivers/isdn/hisax/siemens.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/siemens.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,71 @@
+/* $Id: siemens.h,v 1.4 1997/01/21 22:24:33 keil Exp $
+ *
+ * siemens.h   ISAC and HSCX spezific Macros
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: siemens.h,v $
+ * Revision 1.4  1997/01/21 22:24:33  keil
+ * cleanups
+ *
+ * Revision 1.3  1996/12/08 19:48:34  keil
+ * adding Monitor channel registers
+ *
+ * Revision 1.2  1996/10/27 22:24:00  keil
+ * HSCX version code removed
+ *
+ * Revision 1.1  1996/10/12 21:39:39  keil
+ * Initial revision
+ *
+ *
+*/
+
+
+/* All Registers without FIFOs (original Siemens Spec - 20 hex) */
+
+#define ISAC_MASK 0x0
+#define ISAC_ISTA 0x0
+#define ISAC_STAR 0x1
+#define ISAC_CMDR 0x1
+#define ISAC_EXIR 0x4
+#define ISAC_RBCH 0xa
+#define ISAC_ADF2 0x19
+#define ISAC_SPCR 0x10
+#define ISAC_ADF1 0x18
+#define ISAC_CIR0 0x11
+#define ISAC_CIX0 0x11
+#define ISAC_STCR 0x17
+#define ISAC_MODE 0x2
+#define ISAC_RSTA 0x7
+#define ISAC_RBCL 0x5
+#define ISAC_TIMR 0x3
+#define ISAC_SQXR 0x1b
+#define ISAC_MOSR 0x1a
+#define ISAC_MOCR 0x1a
+#define ISAC_MOR0 0x12
+#define ISAC_MOX0 0x12
+#define ISAC_MOR1 0x14
+#define ISAC_MOX1 0x14
+
+#define HSCX_ISTA 0x0
+#define HSCX_CCR1 0xf
+#define HSCX_CCR2 0xc
+#define HSCX_TSAR 0x11
+#define HSCX_TSAX 0x10
+#define HSCX_XCCR 0x12
+#define HSCX_RCCR 0x13
+#define HSCX_MODE 0x2
+#define HSCX_CMDR 0x1
+#define HSCX_EXIR 0x4
+#define HSCX_XAD1 0x4
+#define HSCX_XAD2 0x5
+#define HSCX_RAH2 0x7
+#define HSCX_RSTA 0x7
+#define HSCX_TIMR 0x3
+#define HSCX_STAR 0x1
+#define HSCX_RBCL 0x5
+#define HSCX_XBCH 0xd
+#define HSCX_VSTR 0xe
+#define HSCX_RLCR 0xe
+#define HSCX_MASK 0x0
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/tei.c linux/drivers/isdn/hisax/tei.c
--- v2.1.26/linux/drivers/isdn/hisax/tei.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/tei.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,314 @@
+/* $Id: tei.c,v 1.6 1997/02/09 00:25:12 keil Exp $
+
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ * $Log: tei.c,v $
+ * Revision 1.6  1997/02/09 00:25:12  keil
+ * new interface handling, one interface per card
+ *
+ * Revision 1.5  1997/01/27 15:57:51  keil
+ * cosmetics
+ *
+ * Revision 1.4  1997/01/21 22:32:44  keil
+ * Tei verify request
+ *
+ * Revision 1.3  1997/01/04 13:45:02  keil
+ * cleanup,adding remove tei request (thanks to Sim Yskes)
+ *
+ * Revision 1.2  1996/12/08 19:52:39  keil
+ * minor debug fix
+ *
+ * Revision 1.1  1996/10/13 20:04:57  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+
+const char *tei_revision = "$Revision: 1.6 $";
+
+static struct PStack *
+findces(struct PStack *st, int ces)
+{
+	struct PStack *ptr = *(st->l1.stlistp);
+
+	while (ptr)
+		if (ptr->l2.ces == ces)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+static struct PStack *
+findtei(struct PStack *st, int tei)
+{
+	struct PStack *ptr = *(st->l1.stlistp);
+
+	if (tei == 127)
+		return (NULL);
+
+	while (ptr)
+		if (ptr->l2.tei == tei)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+static void
+mdl_unit_data_res(struct PStack *st, unsigned int ri, byte mt, byte ai)
+{
+	struct BufHeader *ibh;
+	byte *bp;
+
+	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 7))
+		return;
+	bp = DATAPTR(ibh);
+	bp += 3;
+	bp[0] = 0xf;
+	bp[1] = ri >> 8;
+	bp[2] = ri & 0xff;
+	bp[3] = mt;
+	bp[4] = (ai << 1) | 1;
+	ibh->datasize = 8;
+	st->l3.l3l2(st, DL_UNIT_DATA, ibh);
+}
+
+static void
+mdl_unit_data_ind(struct PStack *st, unsigned int ri, byte mt, byte ai)
+{
+	unsigned int tces;
+	struct PStack *otsp, *ptr;
+	char tmp[64];
+
+	switch (mt) {
+		case (2):
+			tces = ri;
+			if (st->l3.debug) {
+				sprintf(tmp, "identity assign ces %d ai %d", tces, ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if ((otsp = findces(st, tces))) {
+				if (st->l3.debug) {
+					sprintf(tmp, "ces %d --> tei %d", tces, ai);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				otsp->ma.teil2(otsp, MDL_ASSIGN, (void *) (int) ai);
+			}
+			break;
+		case (3):
+			tces = ri;
+			if (st->l3.debug) {
+				sprintf(tmp, "identity denied for ces %d ai %d", tces, ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if ((otsp = findces(st, tces))) {
+				if (st->l3.debug) {
+					sprintf(tmp, "ces %d denied tei %d", tces, ai);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				otsp->l2.tei = 255;
+				otsp->l2.ces = randomces();
+				otsp->ma.teil2(otsp, MDL_REMOVE, 0);
+			}
+			break;
+		case (4):
+			if (st->l3.debug) {
+				sprintf(tmp, "checking identity for %d", ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if (ai == 0x7f) {
+				ptr = *(st->l1.stlistp);
+				while (ptr) {
+					if ((ptr->l2.tei & 0x7f) != 0x7f) {
+						if (st->l3.debug) {
+							sprintf(tmp, "check response for ces %d with tei %d",
+								ptr->l2.ces, ptr->l2.tei);
+							st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+						}
+						/* send identity check response (user->network) */
+						mdl_unit_data_res(st, ptr->l2.ces, 5, ptr->l2.tei);
+					}
+					ptr = ptr->next;
+				}
+			} else {
+				otsp = findtei(st, ai);
+				if (!otsp)
+					break;
+				if (st->l3.debug) {
+					sprintf(tmp, "check response for ces %d with tei %d",
+					     otsp->l2.ces, otsp->l2.tei);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				/* send identity check response (user->network) */
+				mdl_unit_data_res(st, otsp->l2.ces, 5, otsp->l2.tei);
+			}
+			break;
+		case (6):
+			if (st->l3.debug) {
+				sprintf(tmp, "removal for %d", ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+			if (ai == 0x7f) {
+				ptr = *(st->l1.stlistp);
+				while (ptr) {
+					if ((ptr->l2.tei & 0x7f) != 0x7f) {
+						if (st->l3.debug) {
+							sprintf(tmp, "rem ces %d with tei %d",
+								ptr->l2.ces, ptr->l2.tei);
+							st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+						}
+						ptr->ma.teil2(ptr, MDL_REMOVE, 0);
+					}
+					ptr = ptr->next;
+				}
+			} else {
+				otsp = findtei(st, ai);
+				if (!otsp)
+					break;
+				if (st->l3.debug) {
+					sprintf(tmp, "rem ces %d with tei %d",
+					     otsp->l2.ces, otsp->l2.tei);
+					st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+				}
+				otsp->ma.teil2(otsp, MDL_REMOVE, 0);
+			}
+			break;
+		default:
+			if (st->l3.debug) {
+				sprintf(tmp, "message unknown %d ai %d", mt, ai);
+				st->l2.l2m.printdebug(&st->l2.l2m, tmp);
+			}
+	}
+}
+
+void
+tei_handler(struct PStack *st,
+	    byte pr, struct BufHeader *ibh)
+{
+	byte *bp;
+	unsigned int data;
+	char tmp[32];
+
+	switch (pr) {
+		case (MDL_ASSIGN):
+			data = (unsigned int) ibh;
+			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) ibh;
+			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 = DATAPTR(ibh);
+			bp += 3;
+			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]);
+				BufPoolRelease(ibh);
+				break;
+			}
+			mdl_unit_data_ind(st, (bp[1] << 8) | bp[2], bp[3], bp[4] >> 1);
+			BufPoolRelease(ibh);
+			break;
+		default:
+			break;
+	}
+}
+
+unsigned int
+randomces(void)
+{
+	int x = jiffies & 0xffff;
+
+	return (x);
+}
+
+static void
+tei_man(struct PStack *sp, int i, void *v)
+{
+
+	printk(KERN_DEBUG "tei_man\n");
+}
+
+static void
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *sp = st->l1.hardware;
+
+	tei_handler(sp->teistack, pr, arg);
+}
+
+void
+setstack_tei(struct PStack *st)
+{
+	st->l2.l2tei = tei_l2tei;
+}
+
+void
+init_tei(struct IsdnCardState *sp, int protocol)
+{
+	struct PStack *st;
+	char tmp[128];
+
+	st = (struct PStack *) Smalloc(sizeof(struct PStack), GFP_KERNEL,
+				       "struct PStack");
+	setstack_HiSax(st, sp);
+	st->l2.extended = !0;
+	st->l2.laptype = LAPD;
+	st->l2.window = 1;
+	st->l2.orig = !0;
+	st->protocol = protocol;
+/*
+ * the following is not necessary for tei mng. (broadcast only)
+ */
+
+	st->l2.t200 = 500;	/* 500 milliseconds */
+	st->l2.n200 = 4;	/* try 4 times */
+
+	st->l2.sap = 63;
+	st->l2.tei = 127;
+
+	sprintf(tmp, "Card %d tei", sp->cardnr + 1);
+	setstack_isdnl2(st, tmp);
+	st->l2.debug = 0;
+	st->l3.debug = 0;
+
+	st->ma.manl2(st, MDL_NOTEIPROC, NULL);
+
+	st->l2.l2l3 = (void *) tei_handler;
+	st->l1.l1man = tei_man;
+	st->l2.l2man = tei_man;
+	st->l4.l2writewakeup = NULL;
+
+	HiSax_addlist(sp, st);
+	sp->teistack = st;
+}
+
+void
+release_tei(struct IsdnCardState *sp)
+{
+	struct PStack *st = sp->teistack;
+
+	HiSax_rmlist(sp, st);
+	Sfree((void *) st);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/teles0.c linux/drivers/isdn/hisax/teles0.c
--- v2.1.26/linux/drivers/isdn/hisax/teles0.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles0.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,991 @@
+/* $Id: teles0.c,v 1.6 1997/01/27 15:52:18 keil Exp $
+
+ * teles0.c     low level stuff for Teles Memory IO isdn cards
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ * $Log: teles0.c,v $
+ * Revision 1.6  1997/01/27 15:52:18  keil
+ * SMP proof,cosmetics
+ *
+ * Revision 1.5  1997/01/21 22:25:59  keil
+ * cleanups
+ *
+ * Revision 1.4  1996/11/05 19:41:27  keil
+ * more changes for 2.1
+ *
+ * Revision 1.3  1996/10/30 10:22:58  keil
+ * Changes for 2.1 kernels
+ *
+ * Revision 1.2  1996/10/27 22:08:34  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:58  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "teles0.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+
+const char *teles0_revision = "$Revision: 1.6 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline byte
+readisac(unsigned int adr, byte off)
+{
+	return readb(adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+writeisac(unsigned int adr, byte off, byte data)
+{
+	writeb(data, adr + 0x120 + ((off & 1) ? 0x1ff : 0) + off);
+}
+
+
+static inline byte
+readhscx(unsigned int adr, int hscx, byte off)
+{
+	return readb(adr + (hscx ? 0x1e0 : 0x1a0) +
+		     ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+writehscx(unsigned int adr, int hscx, byte off, byte data)
+{
+	writeb(data, adr + (hscx ? 0x1e0 : 0x1a0) +
+	       ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, byte * data, int size)
+{
+	register int i;
+	register byte *ad = (byte *) (adr + 0x100);
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static void
+write_fifo_isac(unsigned int adr, byte * data, int size)
+{
+	register int i;
+	register byte *ad = (byte *) (adr + 0x100);
+	for (i = 0; i < size; i++)
+		writeb(data[i], ad);
+}
+
+static inline void
+read_fifo_hscx(unsigned int adr, int hscx, byte * data, int size)
+{
+	register int i;
+	register byte *ad = (byte *) (adr + (hscx ? 0x1c0 : 0x180));
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, byte * data, int size)
+{
+	int i;
+	register byte *ad = (byte *) (adr + (hscx ? 0x1c0 : 0x180));
+	for (i = 0; i < size; i++)
+		writeb(data[i], ad);
+}
+static inline void
+waitforCEC(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((readhscx(adr, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles0: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr, int hscx)
+{
+	int to = 50;
+
+	while ((!(readhscx(adr, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles0: waitforXFW timeout\n");
+}
+
+static inline void
+writehscxCMDR(int adr, int hscx, byte data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr, hscx);
+	writehscx(adr, hscx, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readhscx(sp->membase, hscx, HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readhscx(sp->membase, hscx, HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readhscx(sp->membase, hscx, HSCX_EXIR));
+}
+
+void
+teles0_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readisac(sp->membase, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readisac(sp->membase, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readisac(sp->membase, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	byte *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh = hsp->rcvibh;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+					      HSCX_RBUF_BPPS)) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->rcvptr;
+
+	hsp->rcvptr += count;
+	save_flags(flags);
+	cli();
+	read_fifo_hscx(sp->membase, hsp->hscx, ptr, count);
+	writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh;
+	int more, count;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	ibh = hsp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - hsp->sendptr;
+	if (count <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->sendptr;
+	hsp->sendptr += count;
+
+	waitforXFW(sp->membase, hsp->hscx);
+	save_flags(flags);
+	cli();
+	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, byte val, byte hscx)
+{
+	byte r;
+	struct HscxState *hsp = sp->hs + hscx;
+	int count, err;
+	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");
+			if (hsp->rcvibh)
+				BufPoolRelease(hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+			goto afterRME;
+		}
+		if (!hsp->rcvibh)
+			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+				       GFP_ATOMIC, (void *) 1, 1)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RME out of buffers");
+				writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+				goto afterRME;
+			} else
+				hsp->rcvptr = 0;
+
+		count = readhscx(sp->membase, hsp->hscx, HSCX_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		hscx_empty_fifo(hsp, count);
+		hsp->rcvibh->datasize = hsp->rcvptr - 1;
+		BufQueueLink(&hsp->rq, hsp->rcvibh);
+		hsp->rcvibh = NULL;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!hsp->rcvibh) {
+			if (hsp->mode == 1)
+				err = BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+			else
+				err = BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+
+			if (err) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RPF out of buffers");
+				writehscxCMDR(sp->membase, hsp->hscx, 0x80);
+				goto afterRPF;
+			} else
+				hsp->rcvptr = 0;
+		}
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			hsp->rcvibh->datasize = hsp->rcvptr;
+			BufQueueLink(&hsp->rq, hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+      afterRPF:
+	if (val & 0x10) {	/* XPR */
+		if (hsp->xmtibh)
+			if (hsp->xmtibh->datasize > hsp->sendptr) {
+				hscx_fill_fifo(hsp);
+				goto afterXPR;
+			} else {
+				if (hsp->releasebuf)
+					BufPoolRelease(hsp->xmtibh);
+				hsp->sendptr = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->xmtibh = NULL;
+			}
+		if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+			hsp->releasebuf = !0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+      afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	byte *ptr;
+	struct BufHeader *ibh = sp->rcvibh;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_empty_fifo");
+
+	if (sp->rcvptr >= 3072) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo rcvptr %d", sp->rcvptr);
+			debugl1(sp, tmp);
+		}
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->rcvptr;
+	sp->rcvptr += count;
+
+	save_flags(flags);
+	cli();
+	read_fifo_isac(sp->membase, ptr, count);
+	writeisac(sp->membase, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh;
+	int count, more;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	ibh = sp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - sp->sendptr;
+	if (count <= 0)
+		return;
+	if (count >= 3072)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->sendptr;
+	sp->sendptr += count;
+
+	save_flags(flags);
+	cli();
+	write_fifo_isac(sp->membase, ptr, count);
+	writeisac(sp->membase, ISAC_CMDR, more ? 0x8 : 0xa);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+ph_command(struct IsdnCardState *sp, unsigned int command)
+{
+	if (sp->debug & L1_DEB_ISAC) {
+		char tmp[32];
+		sprintf(tmp, "ph_command %d", command);
+		debugl1(sp, tmp);
+	}
+	writeisac(sp->membase, ISAC_CIX0, (command << 2) | 3);
+}
+
+static inline void
+isac_interrupt(struct IsdnCardState *sp, byte val)
+{
+	byte exval;
+	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");
+			if (sp->rcvibh)
+				BufPoolRelease(sp->rcvibh);
+			sp->rcvibh = NULL;
+			writeisac(sp->membase, ISAC_CMDR, 0x80);
+			goto afterRME;
+		}
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 3)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writeisac(sp->membase, ISAC_CMDR, 0x80);
+				goto afterRME;
+			} else
+				sp->rcvptr = 0;
+		count = readisac(sp->membase, ISAC_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		isac_empty_fifo(sp, count);
+		sp->rcvibh->datasize = sp->rcvptr;
+		BufQueueLink(&(sp->rq), sp->rcvibh);
+		sp->rcvibh = NULL;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 4)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writeisac(sp->membase, ISAC_CMDR, 0x80);
+				goto afterRPF;
+			} else
+				sp->rcvptr = 0;
+		isac_empty_fifo(sp, 32);
+	}
+      afterRPF:
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->xmtibh)
+			if (sp->xmtibh->datasize > sp->sendptr) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				if (sp->releasebuf)
+					BufPoolRelease(sp->xmtibh);
+				sp->xmtibh = NULL;
+				sp->sendptr = 0;
+			}
+		if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+			sp->releasebuf = !0;
+			isac_fill_fifo(sp);
+		} else
+			isac_sched_event(sp, ISAC_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		sp->ph_state = (readisac(sp->membase, ISAC_CIX0) >> 2)
+		    & 0xf;
+		if (sp->debug & L1_DEB_ISAC) {
+			sprintf(tmp, "l1state %d", sp->ph_state);
+			debugl1(sp, tmp);
+		}
+		isac_new_ph(sp);
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = readisac(sp->membase, ISAC_EXIR);
+		if (sp->debug & L1_DEB_WARN) {
+			sprintf(tmp, "ISAC EXIR %02x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *sp, byte val)
+{
+
+	byte 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.
+				 */
+				hsp->sendptr = 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.
+				 */
+				hsp->sendptr = 0;
+				writehscxCMDR(sp->membase, hsp->hscx, 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readhscx(sp->membase, 0, HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	byte val, stat = 0;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Teles0: Spurious interrupt!\n");
+		return;
+	}
+	val = readhscx(sp->membase, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+		stat |= 1;
+	}
+	val = readisac(sp->membase, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+		stat |= 2;
+	}
+	val = readhscx(sp->membase, 1, HSCX_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readisac(sp->membase, ISAC_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writehscx(sp->membase, 0, HSCX_MASK, 0xFF);
+		writehscx(sp->membase, 1, HSCX_MASK, 0xFF);
+		writehscx(sp->membase, 0, HSCX_MASK, 0x0);
+		writehscx(sp->membase, 1, HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writeisac(sp->membase, ISAC_MASK, 0xFF);
+		writeisac(sp->membase, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->membase;
+
+	/* 16.0 IOM 1 Mode */
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_ADF2, 0x0);
+	writeisac(adr, ISAC_SPCR, 0xa);
+	writeisac(adr, ISAC_ADF1, 0x2);
+	writeisac(adr, ISAC_STCR, 0x70);
+	writeisac(adr, ISAC_MODE, 0xc9);
+	writeisac(adr, ISAC_CMDR, 0x41);
+	writeisac(adr, ISAC_CIX0, (1 << 2) | 3);
+	writeisac(adr, ISAC_MASK, 0xff);
+	writeisac(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writehscx(sp->membase, hscx, HSCX_CCR1, 0x85);
+	writehscx(sp->membase, hscx, HSCX_XAD1, 0xFF);
+	writehscx(sp->membase, hscx, HSCX_XAD2, 0xFF);
+	writehscx(sp->membase, hscx, HSCX_RAH2, 0xFF);
+	writehscx(sp->membase, hscx, HSCX_XBCH, 0x0);
+
+	/* Switch IOM 1 SSI */
+	if (hscx == 0)
+		ichan = 1 - ichan;
+
+	switch (mode) {
+	case (0):
+		writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+		writehscx(sp->membase, hscx, HSCX_TSAX, 0xff);
+		writehscx(sp->membase, hscx, HSCX_TSAR, 0xff);
+		writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+		writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+		writehscx(sp->membase, hscx, HSCX_MODE, 0x84);
+		break;
+	case (1):
+		if (ichan == 0) {
+			writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+			writehscx(sp->membase, hscx, HSCX_TSAX, 0x7);
+			writehscx(sp->membase, hscx, HSCX_TSAR, 0x7);
+			writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+			writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+		} else {
+			writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+			writehscx(sp->membase, hscx, HSCX_TSAX, 0x3);
+			writehscx(sp->membase, hscx, HSCX_TSAR, 0x3);
+			writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+			writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+		}
+		writehscx(sp->membase, hscx, HSCX_MODE, 0xe4);
+		writehscx(sp->membase, hscx, HSCX_CMDR, 0x41);
+		break;
+	case (2):
+		if (ichan == 0) {
+			writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+			writehscx(sp->membase, hscx, HSCX_TSAX, 0x7);
+			writehscx(sp->membase, hscx, HSCX_TSAR, 0x7);
+			writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+			writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+		} else {
+			writehscx(sp->membase, hscx, HSCX_CCR2, 0x30);
+			writehscx(sp->membase, hscx, HSCX_TSAX, 0x3);
+			writehscx(sp->membase, hscx, HSCX_TSAR, 0x3);
+			writehscx(sp->membase, hscx, HSCX_XCCR, 7);
+			writehscx(sp->membase, hscx, HSCX_RCCR, 7);
+		}
+		writehscx(sp->membase, hscx, HSCX_MODE, 0x8c);
+		writehscx(sp->membase, hscx, HSCX_CMDR, 0x41);
+		break;
+	}
+	writehscx(sp->membase, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+release_io_teles0(struct IsdnCard *card)
+{
+	if (card->sp->cfg_reg)
+		release_region(card->sp->cfg_reg, 8);
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = readhscx(sp->membase, 1, HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readhscx(sp->membase, 1, HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = readhscx(sp->membase, 0, HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = readhscx(sp->membase, 0, HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = readhscx(sp->membase, 1, HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = readhscx(sp->membase, 0, HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = readisac(sp->membase, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readisac(sp->membase, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = readisac(sp->membase, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	writeisac(sp->membase, ISAC_MASK, 0);
+	writeisac(sp->membase, ISAC_CMDR, 0x41);
+}
+
+int
+initteles0(struct IsdnCardState *sp)
+{
+	int ret;
+	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);
+		sprintf(tmp, "IRQ %d count %d", sp->irq,
+			kstat.interrupts[sp->irq]);
+		debugl1(sp, tmp);
+		if (kstat.interrupts[sp->irq] == sp->counter) {
+			printk(KERN_WARNING
+			       "Teles0: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_teles0(struct IsdnCard *card)
+{
+	byte cfval, val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, teles0_revision);
+	printk(KERN_NOTICE "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((sp->typ != ISDN_CTYPE_16_0) && (sp->typ != ISDN_CTYPE_8_0))
+		return (0);
+
+	if (sp->typ == ISDN_CTYPE_16_0)
+		sp->cfg_reg = card->para[2];
+	else			/* 8.0 */
+		sp->cfg_reg = 0;
+
+	if (card->para[1] < 0x10000) {
+		card->para[1] <<= 4;
+		printk(KERN_INFO
+		   "Teles0: membase configured DOSish, assuming 0x%lx\n",
+		       (unsigned long) card->para[1]);
+	}
+	sp->membase = card->para[1];
+	sp->irq = card->para[0];
+	if (sp->cfg_reg) {
+		if (check_region((sp->cfg_reg), 8)) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       sp->cfg_reg,
+			       sp->cfg_reg + 8);
+			return (0);
+		} else {
+			request_region(sp->cfg_reg, 8, "teles cfg");
+		}
+	}
+	switch (sp->irq) {
+	case 2:
+		cfval = 0x00;
+		break;
+	case 3:
+		cfval = 0x02;
+		break;
+	case 4:
+		cfval = 0x04;
+		break;
+	case 5:
+		cfval = 0x06;
+		break;
+	case 10:
+		cfval = 0x08;
+		break;
+	case 11:
+		cfval = 0x0A;
+		break;
+	case 12:
+		cfval = 0x0C;
+		break;
+	case 15:
+		cfval = 0x0E;
+		break;
+	default:
+		cfval = 0x00;
+		break;
+	}
+	cfval |= ((card->para[1] >> 9) & 0xF0);
+	if (sp->cfg_reg) {
+		if ((val = bytein(sp->cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       sp->cfg_reg + 0, val);
+			release_region(sp->cfg_reg, 8);
+			return (0);
+		}
+		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       sp->cfg_reg + 1, val);
+			release_region(sp->cfg_reg, 8);
+			return (0);
+		}
+		val = bytein(sp->cfg_reg + 2);	/* 0x1e=without AB
+						   * 0x1f=with AB
+						   * 0x1c 16.3 ???
+						 */
+		if (val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       sp->cfg_reg + 2, val);
+			release_region(sp->cfg_reg, 8);
+			return (0);
+		}
+		save_flags(flags);
+		byteout(sp->cfg_reg + 4, cfval);
+		sti();
+		HZDELAY(HZ / 10 + 1);
+		byteout(sp->cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
+		restore_flags(flags);
+	}
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d mem:%x cfg:%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->membase, sp->cfg_reg);
+	verA = readhscx(sp->membase, 0, HSCX_VSTR) & 0xf;
+	verB = readhscx(sp->membase, 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "Teles0: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = readisac(sp->membase, ISAC_RBCH);
+	printk(KERN_INFO "Teles0: ISAC %s\n",
+	       ISACVersion(val));
+
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		 "Teles0: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_teles0(card);
+		return (0);
+	}
+	save_flags(flags);
+	writeb(0, sp->membase + 0x80);
+	sti();
+	HZDELAY(HZ / 5 + 1);
+	writeb(1, sp->membase + 0x80);
+	HZDELAY(HZ / 5 + 1);
+	restore_flags(flags);
+
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/teles0.h linux/drivers/isdn/hisax/teles0.h
--- v2.1.26/linux/drivers/isdn/hisax/teles0.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles0.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,21 @@
+/* $Id: teles0.h,v 1.2 1997/01/21 22:26:52 keil Exp $
+ *
+ * teles0.h   Header for Teles 16.0 8.0 & compatible
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: teles0.h,v $
+ * Revision 1.2  1997/01/21 22:26:52  keil
+ * cleanups
+ *
+ * Revision 1.1  1996/10/13 20:03:48  keil
+ * Initial revision
+ *
+ *
+*/
+
+extern	void teles0_report(struct IsdnCardState *sp);
+extern  void release_io_teles0(struct IsdnCard *card);
+extern	int  setup_teles0(struct IsdnCard *card);
+extern  int  initteles0(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/teles3.c linux/drivers/isdn/hisax/teles3.c
--- v2.1.26/linux/drivers/isdn/hisax/teles3.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles3.c	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,1037 @@
+/* $Id: teles3.c,v 1.7 1997/01/28 22:48:33 keil Exp $
+
+ * teles3.c     low level stuff for Teles 16.3 & PNP isdn cards
+ *
+ *              based on the teles driver from Jan den Ouden
+ *
+ * Author       Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ * $Log: teles3.c,v $
+ * Revision 1.7  1997/01/28 22:48:33  keil
+ * fixes for Teles PCMCIA (Christof Petig)
+ *
+ * Revision 1.6  1997/01/27 15:52:55  keil
+ * SMP proof,cosmetics, PCMCIA added
+ *
+ * Revision 1.5  1997/01/21 22:28:32  keil
+ * cleanups
+ *
+ * Revision 1.4  1996/12/14 21:05:41  keil
+ * Reset for 16.3 PnP
+ *
+ * Revision 1.3  1996/11/05 19:56:54  keil
+ * debug output fixed
+ *
+ * Revision 1.2  1996/10/27 22:09:15  keil
+ * cosmetic changes
+ *
+ * Revision 1.1  1996/10/13 20:04:59  keil
+ * Initial revision
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "siemens.h"
+#include "hisax.h"
+#include "teles3.h"
+#include "isdnl1.h"
+#include <linux/kernel_stat.h>
+
+extern const char *CardType[];
+const char *teles3_revision = "$Revision: 1.7 $";
+
+#define byteout(addr,val) outb_p(val,addr)
+#define bytein(addr) inb_p(addr)
+
+static inline byte
+readreg(unsigned int adr, byte off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, byte off, byte data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, byte * data, int size)
+{
+	insb(adr + 0x1e, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, byte * data, int size)
+{
+	outsb(adr + 0x1e, data, size);
+}
+
+static inline void
+waitforCEC(int adr)
+{
+	int to = 50;
+
+	while ((readreg(adr, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles3: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(int adr)
+{
+	int to = 50;
+
+	while ((!(readreg(adr, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "Teles3: waitforXFW timeout\n");
+}
+static inline void
+writehscxCMDR(int adr, byte data)
+{
+	long flags;
+
+	save_flags(flags);
+	cli();
+	waitforCEC(adr);
+	writereg(adr, HSCX_CMDR, data);
+	restore_flags(flags);
+}
+
+/*
+ * fast interrupt here
+ */
+
+static void
+hscxreport(struct IsdnCardState *sp, int hscx)
+{
+	printk(KERN_DEBUG "HSCX %d\n", hscx);
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->hscx[hscx], HSCX_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->hscx[hscx], HSCX_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->hscx[hscx], HSCX_EXIR));
+}
+
+void
+teles3_report(struct IsdnCardState *sp)
+{
+	printk(KERN_DEBUG "ISAC\n");
+	printk(KERN_DEBUG "ISTA %x\n", readreg(sp->isac, ISAC_ISTA));
+	printk(KERN_DEBUG "STAR %x\n", readreg(sp->isac, ISAC_STAR));
+	printk(KERN_DEBUG "EXIR %x\n", readreg(sp->isac, ISAC_EXIR));
+	hscxreport(sp, 0);
+	hscxreport(sp, 1);
+}
+
+/*
+ * HSCX stuff goes here
+ */
+
+static void
+hscx_empty_fifo(struct HscxState *hsp, int count)
+{
+	byte *ptr;
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh = hsp->rcvibh;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_empty_fifo");
+
+	if (hsp->rcvptr + count > BUFFER_SIZE(HSCX_RBUF_ORDER,
+					      HSCX_RBUF_BPPS)) {
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "hscx_empty_fifo: incoming packet too large");
+		writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->rcvptr;
+
+	hsp->rcvptr += count;
+	save_flags(flags);
+	cli();
+	read_fifo(sp->hscx[hsp->hscx], ptr, count);
+	writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_HSCX_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     hsp->hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+hscx_fill_fifo(struct HscxState *hsp)
+{
+	struct IsdnCardState *sp = hsp->sp;
+	struct BufHeader *ibh;
+	int more, count;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_HSCX) && !(sp->debug & L1_DEB_HSCX_FIFO))
+		debugl1(sp, "hscx_fill_fifo");
+
+	ibh = hsp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - hsp->sendptr;
+	if (count <= 0)
+		return;
+
+	more = (hsp->mode == 1) ? 1 : 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += hsp->sendptr;
+	hsp->sendptr += count;
+
+	waitforXFW(sp->hscx[hsp->hscx]);
+	save_flags(flags);
+	cli();
+	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, byte val, byte hscx)
+{
+	byte r;
+	struct HscxState *hsp = sp->hs + hscx;
+	int count, err;
+	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");
+			if (hsp->rcvibh)
+				BufPoolRelease(hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+			goto afterRME;
+		}
+		if (!hsp->rcvibh)
+			if (BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+				       GFP_ATOMIC, (void *) 1, 1)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RME out of buffers");
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+				goto afterRME;
+			} else
+				hsp->rcvptr = 0;
+
+		count = readreg(sp->hscx[hsp->hscx], HSCX_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		hscx_empty_fifo(hsp, count);
+		hsp->rcvibh->datasize = hsp->rcvptr - 1;
+		BufQueueLink(&hsp->rq, hsp->rcvibh);
+		hsp->rcvibh = NULL;
+		hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!hsp->rcvibh) {
+			if (hsp->mode == 1)
+				err = BufPoolGet(&hsp->rcvibh, &hsp->smallpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+			else
+				err = BufPoolGet(&hsp->rcvibh, &hsp->rbufpool,
+					      GFP_ATOMIC, (void *) 1, 2);
+
+			if (err) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "HSCX RPF out of buffers");
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x80);
+				goto afterRPF;
+			} else
+				hsp->rcvptr = 0;
+		}
+		hscx_empty_fifo(hsp, 32);
+		if (hsp->mode == 1) {
+			/* receive audio data */
+			hsp->rcvibh->datasize = hsp->rcvptr;
+			BufQueueLink(&hsp->rq, hsp->rcvibh);
+			hsp->rcvibh = NULL;
+			hscx_sched_event(hsp, HSCX_RCVBUFREADY);
+		}
+	}
+      afterRPF:
+	if (val & 0x10) {	/* XPR */
+		if (hsp->xmtibh)
+			if (hsp->xmtibh->datasize > hsp->sendptr) {
+				hscx_fill_fifo(hsp);
+				goto afterXPR;
+			} else {
+				if (hsp->releasebuf)
+					BufPoolRelease(hsp->xmtibh);
+				hsp->sendptr = 0;
+				if (hsp->st->l4.l1writewakeup)
+					hsp->st->l4.l1writewakeup(hsp->st);
+				hsp->xmtibh = NULL;
+			}
+		if (!BufQueueUnlink(&hsp->xmtibh, &hsp->sq)) {
+			hsp->releasebuf = !0;
+			hscx_fill_fifo(hsp);
+		} else
+			hscx_sched_event(hsp, HSCX_XMTBUFREADY);
+	}
+      afterXPR:
+}
+
+/*
+ * ISAC stuff goes here
+ */
+
+static void
+isac_empty_fifo(struct IsdnCardState *sp, int count)
+{
+	byte *ptr;
+	struct BufHeader *ibh = sp->rcvibh;
+	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->rcvptr >= 3072) {
+		if (sp->debug & L1_DEB_WARN) {
+			char tmp[40];
+			sprintf(tmp, "isac_empty_fifo rcvptr %d", sp->rcvptr);
+			debugl1(sp, tmp);
+		}
+		return;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->rcvptr;
+	sp->rcvptr += count;
+
+	save_flags(flags);
+	cli();
+	read_fifo(sp->isac, ptr, count);
+	writereg(sp->isac, ISAC_CMDR, 0x80);
+	restore_flags(flags);
+	if (sp->debug & L1_DEB_ISAC_FIFO) {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(sp, tmp);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *sp)
+{
+	struct BufHeader *ibh;
+	int count, more;
+	byte *ptr;
+	long flags;
+
+	if ((sp->debug & L1_DEB_ISAC) && !(sp->debug & L1_DEB_ISAC_FIFO))
+		debugl1(sp, "isac_fill_fifo");
+
+	ibh = sp->xmtibh;
+	if (!ibh)
+		return;
+
+	count = ibh->datasize - sp->sendptr;
+	if (count <= 0)
+		return;
+	if (count >= 3072)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = DATAPTR(ibh);
+	ptr += sp->sendptr;
+	sp->sendptr += count;
+
+	save_flags(flags);
+	cli();
+	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, byte val)
+{
+	byte exval;
+	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");
+			if (sp->rcvibh)
+				BufPoolRelease(sp->rcvibh);
+			sp->rcvibh = NULL;
+			writereg(sp->isac, ISAC_CMDR, 0x80);
+			goto afterRME;
+		}
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 3)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writereg(sp->isac, ISAC_CMDR, 0x80);
+				goto afterRME;
+			} else
+				sp->rcvptr = 0;
+		count = readreg(sp->isac, ISAC_RBCL) & 0x1f;
+		if (count == 0)
+			count = 32;
+		isac_empty_fifo(sp, count);
+		sp->rcvibh->datasize = sp->rcvptr;
+		BufQueueLink(&(sp->rq), sp->rcvibh);
+		sp->rcvibh = NULL;
+		isac_sched_event(sp, ISAC_RCVBUFREADY);
+	}
+      afterRME:
+	if (val & 0x40) {	/* RPF */
+		if (!sp->rcvibh)
+			if (BufPoolGet(&(sp->rcvibh), &(sp->rbufpool),
+				       GFP_ATOMIC, (void *) 1, 4)) {
+				if (sp->debug & L1_DEB_WARN)
+					debugl1(sp, "ISAC RME out of buffers!");
+				writereg(sp->isac, ISAC_CMDR, 0x80);
+				goto afterRPF;
+			} else
+				sp->rcvptr = 0;
+		isac_empty_fifo(sp, 32);
+	}
+      afterRPF:
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (sp->debug & L1_DEB_WARN)
+			debugl1(sp, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (sp->xmtibh)
+			if (sp->xmtibh->datasize > sp->sendptr) {
+				isac_fill_fifo(sp);
+				goto afterXPR;
+			} else {
+				if (sp->releasebuf)
+					BufPoolRelease(sp->xmtibh);
+				sp->xmtibh = NULL;
+				sp->sendptr = 0;
+			}
+		if (!BufQueueUnlink(&sp->xmtibh, &sp->sq)) {
+			sp->releasebuf = !0;
+			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, byte val)
+{
+
+	byte 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.
+				 */
+				hsp->sendptr = 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.
+				 */
+				hsp->sendptr = 0;
+				writehscxCMDR(sp->hscx[hsp->hscx], 0x01);
+				if (sp->debug & L1_DEB_WARN) {
+					sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
+					debugl1(sp, tmp);
+				}
+			}
+		} else if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A EXIR %x", exval);
+			debugl1(sp, tmp);
+		}
+	}
+	if (val & 0x04) {
+		exval = readreg(sp->hscx[0], HSCX_ISTA);
+		if (sp->debug & L1_DEB_HSCX) {
+			sprintf(tmp, "HSCX A interrupt %x", exval);
+			debugl1(sp, tmp);
+		}
+		hscx_interrupt(sp, exval, 0);
+	}
+}
+
+static void
+teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *sp;
+	byte val, stat = 0;
+
+	sp = (struct IsdnCardState *) irq2dev_map[intno];
+
+	if (!sp) {
+		printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+		return;
+	}
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(sp, val);
+		stat |= 1;
+	}
+	val = readreg(sp->isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(sp, val);
+		stat |= 2;
+	}
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_HSCX)
+			debugl1(sp, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(sp->isac, ISAC_ISTA);
+	if (val) {
+		if (sp->debug & L1_DEB_ISAC)
+			debugl1(sp, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (stat & 1) {
+		writereg(sp->hscx[0], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[1], HSCX_MASK, 0xFF);
+		writereg(sp->hscx[0], HSCX_MASK, 0x0);
+		writereg(sp->hscx[1], HSCX_MASK, 0x0);
+	}
+	if (stat & 2) {
+		writereg(sp->isac, ISAC_MASK, 0xFF);
+		writereg(sp->isac, ISAC_MASK, 0x0);
+	}
+}
+
+
+static void
+initisac(struct IsdnCardState *sp)
+{
+	unsigned int adr = sp->isac;
+
+	/* 16.3 IOM 2 Mode */
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_ADF2, 0x80);
+	writereg(adr, ISAC_SQXR, 0x2f);
+	writereg(adr, ISAC_SPCR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x2);
+	writereg(adr, ISAC_STCR, 0x70);
+	writereg(adr, ISAC_MODE, 0xc9);
+	writereg(adr, ISAC_TIMR, 0x0);
+	writereg(adr, ISAC_ADF1, 0x0);
+	writereg(adr, ISAC_CMDR, 0x41);
+	writereg(adr, ISAC_CIX0, (1 << 2) | 3);
+	writereg(adr, ISAC_MASK, 0xff);
+	writereg(adr, ISAC_MASK, 0x0);
+}
+
+static void
+modehscx(struct HscxState *hs, int mode, int ichan)
+{
+	struct IsdnCardState *sp = hs->sp;
+	int hscx = hs->hscx;
+
+	if (sp->debug & L1_DEB_HSCX) {
+		char tmp[40];
+		sprintf(tmp, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, ichan);
+		debugl1(sp, tmp);
+	}
+	hs->mode = mode;
+	writereg(sp->hscx[hscx], HSCX_CCR1, 0x85);
+	writereg(sp->hscx[hscx], HSCX_XAD1, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XAD2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_RAH2, 0xFF);
+	writereg(sp->hscx[hscx], HSCX_XBCH, 0x0);
+	writereg(sp->hscx[hscx], HSCX_RLCR, 0x0);
+
+	switch (mode) {
+	case (0):
+		writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+		writereg(sp->hscx[hscx], HSCX_TSAX, 0xff);
+		writereg(sp->hscx[hscx], HSCX_TSAR, 0xff);
+		writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+		writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		writereg(sp->hscx[hscx], HSCX_MODE, 0x84);
+		break;
+	case (1):
+		if (ichan == 0) {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		} else {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		}
+		writereg(sp->hscx[hscx], HSCX_MODE, 0xe4);
+		writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+		break;
+	case (2):
+		if (ichan == 0) {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x2f);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		} else {
+			writereg(sp->hscx[hscx], HSCX_CCR2, 0x30);
+			writereg(sp->hscx[hscx], HSCX_TSAX, 0x3);
+			writereg(sp->hscx[hscx], HSCX_TSAR, 0x3);
+			writereg(sp->hscx[hscx], HSCX_XCCR, 7);
+			writereg(sp->hscx[hscx], HSCX_RCCR, 7);
+		}
+		writereg(sp->hscx[hscx], HSCX_MODE, 0x8c);
+		writereg(sp->hscx[hscx], HSCX_CMDR, 0x41);
+		break;
+	}
+	writereg(sp->hscx[hscx], HSCX_ISTA, 0x00);
+}
+
+inline static void
+release_ioregs(struct IsdnCard *card, int mask)
+{
+	if (mask & 1)
+		release_region(card->sp->isac, 32);
+	if (mask & 2)
+		release_region(card->sp->hscx[0], 32);
+	if (mask & 4)
+		release_region(card->sp->hscx[1], 32);
+}
+
+void
+release_io_teles3(struct IsdnCard *card)
+{
+	if (card->sp->typ == ISDN_CTYPE_TELESPCMCIA)
+		release_region(card->sp->hscx[0], 97);
+	else {
+		if (card->sp->cfg_reg)
+			release_region(card->sp->cfg_reg, 8);
+		release_ioregs(card, 0x7);
+	}
+}
+
+static void
+clear_pending_ints(struct IsdnCardState *sp)
+{
+	int val;
+	char tmp[64];
+
+	val = readreg(sp->hscx[1], HSCX_ISTA);
+	sprintf(tmp, "HSCX B ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->hscx[1], HSCX_EXIR);
+		sprintf(tmp, "HSCX B EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x02) {
+		val = readreg(sp->hscx[0], HSCX_EXIR);
+		sprintf(tmp, "HSCX A EXIR %x", val);
+		debugl1(sp, tmp);
+	}
+	val = readreg(sp->hscx[0], HSCX_ISTA);
+	sprintf(tmp, "HSCX A ISTA %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[1], HSCX_STAR);
+	sprintf(tmp, "HSCX B STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->hscx[0], HSCX_STAR);
+	sprintf(tmp, "HSCX A STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_STAR);
+	sprintf(tmp, "ISAC STAR %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_MODE);
+	sprintf(tmp, "ISAC MODE %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ADF2);
+	sprintf(tmp, "ISAC ADF2 %x", val);
+	debugl1(sp, tmp);
+	val = readreg(sp->isac, ISAC_ISTA);
+	sprintf(tmp, "ISAC ISTA %x", val);
+	debugl1(sp, tmp);
+	if (val & 0x01) {
+		val = readreg(sp->isac, ISAC_EXIR);
+		sprintf(tmp, "ISAC EXIR %x", val);
+		debugl1(sp, tmp);
+	} else if (val & 0x04) {
+		val = readreg(sp->isac, ISAC_CIR0);
+		sprintf(tmp, "ISAC CIR0 %x", val);
+		debugl1(sp, tmp);
+	}
+	writereg(sp->isac, ISAC_MASK, 0);
+	writereg(sp->isac, ISAC_CMDR, 0x41);
+}
+
+int
+initteles3(struct IsdnCardState *sp)
+{
+	int ret;
+	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);
+		sp->modehscx(sp->hs + 1, 0, 0);
+		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
+			       "Teles3: IRQ(%d) getting no interrupts during init\n",
+			       sp->irq);
+			irq2dev_map[sp->irq] = NULL;
+			free_irq(sp->irq, NULL);
+			return (0);
+		}
+	}
+	return (ret);
+}
+
+int
+setup_teles3(struct IsdnCard *card)
+{
+	byte cfval = 0, val, verA, verB;
+	struct IsdnCardState *sp = card->sp;
+	long flags;
+	char tmp[64];
+
+	strcpy(tmp, teles3_revision);
+	printk(KERN_NOTICE "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((sp->typ != ISDN_CTYPE_16_3) && (sp->typ != ISDN_CTYPE_PNP)
+	    && (sp->typ != ISDN_CTYPE_TELESPCMCIA))
+		return (0);
+
+	if (sp->typ == ISDN_CTYPE_16_3) {
+		sp->cfg_reg = card->para[1];
+		switch (sp->cfg_reg) {
+		case 0x180:
+		case 0x280:
+		case 0x380:
+			sp->cfg_reg |= 0xc00;
+			break;
+		}
+		sp->isac = sp->cfg_reg - 0x400;
+		sp->hscx[0] = sp->cfg_reg - 0xc00;
+		sp->hscx[1] = sp->cfg_reg - 0x800;
+	} else if (sp->typ == ISDN_CTYPE_TELESPCMCIA) {
+		sp->cfg_reg = 0;
+		sp->hscx[0] = card->para[1];
+		sp->hscx[1] = card->para[1] + 0x20;
+		sp->isac = card->para[1] + 0x40;
+	} else {		/* PNP */
+		sp->cfg_reg = 0;
+		sp->isac = card->para[1];
+		sp->hscx[0] = card->para[2];
+		sp->hscx[1] = card->para[2] + 0x20;
+	}
+	sp->irq = card->para[0];
+	if (sp->typ == ISDN_CTYPE_TELESPCMCIA) {
+		if (check_region((sp->hscx[0]), 97)) {
+			printk(KERN_WARNING
+			       "HiSax: %s ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->hscx[0],
+			       sp->hscx[0] + 96);
+			return (0);
+		} else
+			request_region(sp->hscx[0], 97, "HiSax Teles PCMCIA");
+	} else {
+		if (sp->cfg_reg) {
+			if (check_region((sp->cfg_reg), 8)) {
+				printk(KERN_WARNING
+				       "HiSax: %s config port %x-%x already in use\n",
+				       CardType[card->typ],
+				       sp->cfg_reg,
+				       sp->cfg_reg + 8);
+				return (0);
+			} else {
+				request_region(sp->cfg_reg, 8, "teles3 cfg");
+			}
+		}
+		if (check_region((sp->isac), 32)) {
+			printk(KERN_WARNING
+			   "HiSax: %s isac ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->isac,
+			       sp->isac + 32);
+			if (sp->cfg_reg) {
+				release_region(sp->cfg_reg, 8);
+			}
+			return (0);
+		} else {
+			request_region(sp->isac, 32, "HiSax isac");
+		}
+		if (check_region((sp->hscx[0]), 32)) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx A ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->hscx[0],
+			       sp->hscx[0] + 32);
+			if (sp->cfg_reg) {
+				release_region(sp->cfg_reg, 8);
+			}
+			release_ioregs(card, 1);
+			return (0);
+		} else {
+			request_region(sp->hscx[0], 32, "HiSax hscx A");
+		}
+		if (check_region((sp->hscx[1]), 32)) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx B ports %x-%x already in use\n",
+			       CardType[sp->typ],
+			       sp->hscx[1],
+			       sp->hscx[1] + 32);
+			if (sp->cfg_reg) {
+				release_region(sp->cfg_reg, 8);
+			}
+			release_ioregs(card, 3);
+			return (0);
+		} else {
+			request_region(sp->hscx[1], 32, "HiSax hscx B");
+		}
+		switch (sp->irq) {
+		case 2:
+			cfval = 0x00;
+			break;
+		case 3:
+			cfval = 0x02;
+			break;
+		case 4:
+			cfval = 0x04;
+			break;
+		case 5:
+			cfval = 0x06;
+			break;
+		case 10:
+			cfval = 0x08;
+			break;
+		case 11:
+			cfval = 0x0A;
+			break;
+		case 12:
+			cfval = 0x0C;
+			break;
+		case 15:
+			cfval = 0x0E;
+			break;
+		default:
+			cfval = 0x00;
+			break;
+		}
+	}
+	if (sp->cfg_reg) {
+		if ((val = bytein(sp->cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       sp->cfg_reg + 0, val);
+			release_io_teles3(card);
+			return (0);
+		}
+		if ((val = bytein(sp->cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       sp->cfg_reg + 1, val);
+			release_io_teles3(card);
+			return (0);
+		}
+		val = bytein(sp->cfg_reg + 2);	/* 0x1e=without AB
+						   * 0x1f=with AB
+						   * 0x1c 16.3 ???
+						 */
+		if (val != 0x1c && val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       sp->cfg_reg + 2, val);
+			release_io_teles3(card);
+			return (0);
+		}
+		save_flags(flags);
+		byteout(sp->cfg_reg + 4, cfval);
+		sti();
+		HZDELAY(HZ / 10 + 1);
+		byteout(sp->cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
+		restore_flags(flags);
+	} else {
+		/* Reset off for 16.3 PnP , thanks to Georg Acher */
+		save_flags(flags);
+		byteout(sp->isac + 0x1c, 1);
+		HZDELAY(2);
+		restore_flags(flags);
+	}
+	printk(KERN_NOTICE
+	       "HiSax: %s config irq:%d isac:%x  cfg:%x\n",
+	       CardType[sp->typ], sp->irq,
+	       sp->isac, sp->cfg_reg);
+	printk(KERN_NOTICE
+	       "HiSax: hscx A:%x  hscx B:%x\n",
+	       sp->hscx[0], sp->hscx[1]);
+	verA = readreg(sp->hscx[0], HSCX_VSTR) & 0xf;
+	verB = readreg(sp->hscx[1], HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "Teles3: HSCX version A: %s  B: %s\n",
+	       HscxVersion(verA), HscxVersion(verB));
+	val = readreg(sp->isac, ISAC_RBCH);
+	printk(KERN_INFO "Teles3: ISAC %s\n",
+	       ISACVersion(val));
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf)) {
+		printk(KERN_WARNING
+		       "Teles3: wrong HSCX versions check IO address\n");
+		release_io_teles3(card);
+		return (0);
+	}
+	sp->modehscx = &modehscx;
+	sp->ph_command = &ph_command;
+	sp->hscx_fill_fifo = &hscx_fill_fifo;
+	sp->isac_fill_fifo = &isac_fill_fifo;
+	return (1);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/hisax/teles3.h linux/drivers/isdn/hisax/teles3.h
--- v2.1.26/linux/drivers/isdn/hisax/teles3.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/hisax/teles3.h	Tue Feb 25 17:12:49 1997
@@ -0,0 +1,21 @@
+/* $Id: teles3.h,v 1.2 1997/01/21 22:27:14 keil Exp $
+ *
+ * teles3.h   Header for Teles 16.3 PNP & compatible
+ *
+ * Author	Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: teles3.h,v $
+ * Revision 1.2  1997/01/21 22:27:14  keil
+ * cleanups
+ *
+ * Revision 1.1  1996/10/13 20:03:49  keil
+ * Initial revision
+ *
+ *
+*/
+
+extern	void teles3_report(struct IsdnCardState *sp);
+extern  void release_io_teles3(struct IsdnCard *card);
+extern	int  setup_teles3(struct IsdnCard *card);
+extern  int  initteles3(struct IsdnCardState *sp);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/icn/icn.c linux/drivers/isdn/icn/icn.c
--- v2.1.26/linux/drivers/isdn/icn/icn.c	Fri Dec 27 02:03:22 1996
+++ linux/drivers/isdn/icn/icn.c	Tue Feb 25 17:12:49 1997
@@ -1,5 +1,5 @@
-/* $Id: icn.c,v 1.29 1996/08/29 20:34:54 fritz Exp $
- *
+/* $Id: icn.c,v 1.38 1997/02/11 18:29:31 fritz Exp $
+
  * ISDN low-level module for the ICN active ISDN-Card.
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
@@ -19,6 +19,36 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.c,v $
+ * Revision 1.38  1997/02/11 18:29:31  fritz
+ * Bugfix in D64S initialization.
+ *
+ * Revision 1.37  1997/02/10 22:43:20  fritz
+ * Added plan and screen elements on ISDN_STAT_ICALL
+ *
+ * Revision 1.36  1997/02/10 21:31:20  fritz
+ * Changed setup-interface (incoming and outgoing).
+ *
+ * Revision 1.35  1997/02/10 10:10:28  fritz
+ * Changes for Kernel 2.1.X compatibility.
+ * Enhanced initialization, can recover from
+ * misconfiguration by other autoprobing drivers.
+ *
+ * Revision 1.34  1997/01/29 22:34:44  fritz
+ * Cleanup, Corrected D64S setup of 2nd channel.
+ *
+ * Revision 1.33  1996/12/05 20:31:48  tsbogend
+ * added handling of L2: DATA LINK LOST messages
+ *
+ * Revision 1.32  1996/11/14 23:49:18  fritz
+ * Bugfix: copy_to_user/copy_from_user mismatch in debugging-ioctl.
+ *
+ * Revision 1.31  1996/11/13 02:36:25  fritz
+ * Fixed a race condition in writecmd.
+ * Some optimizations and cleanup.
+ *
+ * Revision 1.30  1996/10/22 23:14:09  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
  * Revision 1.29  1996/08/29 20:34:54  fritz
  * Bugfix in send queue management:
  * sndcount was not updated correctly.
@@ -141,7 +171,7 @@
 #undef MAP_DEBUG
 
 static char
-*revision = "$Revision: 1.29 $";
+*revision = "$Revision: 1.38 $";
 
 static int icn_addcard(int, char *, char *);
 
@@ -150,16 +180,13 @@
  * Parameter:
  *   queue = pointer to queue-head
  */
-static void icn_free_queue(struct sk_buff_head *queue)
+static void
+icn_free_queue(struct sk_buff_head *queue)
 {
-        struct sk_buff *skb;
-        unsigned long flags;
+	struct sk_buff *skb;
 
-        save_flags(flags);
-        cli();
-        while ((skb = skb_dequeue(queue)))
-                dev_kfree_skb(skb, FREE_WRITE);
-        restore_flags(flags);
+	while ((skb = skb_dequeue(queue)))
+		dev_kfree_skb(skb, FREE_WRITE);
 }
 
 /* Put a value into a shift-register, highest bit first.
@@ -169,53 +196,57 @@
  *            firstbit = Bit-Number of highest bit
  *            bitcount = Number of bits to output
  */
-static inline void icn_shiftout(unsigned short port,
-                     unsigned long val,
-                     int firstbit,
-                     int bitcount)
+static inline void
+icn_shiftout(unsigned short port,
+	     unsigned long val,
+	     int firstbit,
+	     int bitcount)
 {
 
-        register u_char s;
-        register u_char c;
+	register u_char s;
+	register u_char c;
 
-        for (s = firstbit, c = bitcount; c > 0; s--, c--)
-                OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
+	for (s = firstbit, c = bitcount; c > 0; s--, c--)
+		OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
 }
 
 /*
  * disable a cards shared memory
  */
-static inline void icn_disable_ram(icn_card *card)
+static inline void
+icn_disable_ram(icn_card * card)
 {
-        OUTB_P(0, ICN_MAPRAM);
+	OUTB_P(0, ICN_MAPRAM);
 }
 
 /*
  * enable a cards shared memory
  */
-static inline void icn_enable_ram(icn_card *card)
+static inline void
+icn_enable_ram(icn_card * card)
 {
-        OUTB_P(0xff, ICN_MAPRAM);
+	OUTB_P(0xff, ICN_MAPRAM);
 }
 
 /*
  * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12)
  */
-static inline void icn_map_channel(icn_card *card, int channel)
+static inline void
+icn_map_channel(icn_card * card, int channel)
 {
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_map_channel %d %d\n", dev->channel, channel);
+	printk(KERN_DEBUG "icn_map_channel %d %d\n", dev->channel, channel);
 #endif
-        if ((channel == dev.channel) && (card == dev.mcard))
-                return;
-        if (dev.mcard)
-                icn_disable_ram(dev.mcard);
-        icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);        /* Select Bank          */
-        icn_enable_ram(card);
-        dev.mcard = card;
-        dev.channel = channel;
+	if ((channel == dev.channel) && (card == dev.mcard))
+		return;
+	if (dev.mcard)
+		icn_disable_ram(dev.mcard);
+	icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);	/* Select Bank          */
+	icn_enable_ram(card);
+	dev.mcard = card;
+	dev.channel = channel;
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_map_channel done\n");
+	printk(KERN_DEBUG "icn_map_channel done\n");
 #endif
 }
 
@@ -224,98 +255,102 @@
  * Return 0 if requested card/channel is unmapped (failure).
  * Return 1 on success.
  */
-static inline int icn_lock_channel(icn_card *card, int channel)
+static inline int
+icn_lock_channel(icn_card * card, int channel)
 {
-        register int retval;
-        ulong flags;
+	register int retval;
+	ulong flags;
 
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
+	printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
 #endif
-        save_flags(flags);
-        cli();
-        if ((dev.channel == channel) && (card == dev.mcard)) {
-                dev.chanlock++;
-                retval = 1;
+	save_flags(flags);
+	cli();
+	if ((dev.channel == channel) && (card == dev.mcard)) {
+		dev.chanlock++;
+		retval = 1;
 #ifdef MAP_DEBUG
-                printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
+		printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
 #endif
-        } else {
-                retval = 0;
+	} else {
+		retval = 0;
 #ifdef MAP_DEBUG
-                printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, device->channel);
+		printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, device->channel);
 #endif
-        }
-        restore_flags(flags);
-        return retval;
+	}
+	restore_flags(flags);
+	return retval;
 }
 
 /*
  * Release current card/channel lock
  */
-static inline void icn_release_channel(void)
+static inline void
+icn_release_channel(void)
 {
-        ulong flags;
+	ulong flags;
 
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "icn_release_channel l=%d\n", device->chanlock);
+	printk(KERN_DEBUG "icn_release_channel l=%d\n", device->chanlock);
 #endif
-        save_flags(flags);
-        cli();
-        if (dev.chanlock)
-                dev.chanlock--;
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (dev.chanlock > 0)
+		dev.chanlock--;
+	restore_flags(flags);
 }
 
 /*
  * Try to map and lock a cards channel.
  * Return 1 on success, 0 on failure.
  */
-static inline int icn_trymaplock_channel(icn_card *card, int channel)
+static inline int
+icn_trymaplock_channel(icn_card * card, int channel)
 {
-        ulong flags;
+	ulong flags;
 
-        save_flags(flags);
-        cli();
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,
-               dev.chanlock);
+	printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,
+	       dev.chanlock);
 #endif
-        if ((!dev.chanlock) ||
-            ((dev.channel == channel) && (dev.mcard == card))) {
-                dev.chanlock++;
-                icn_map_channel(card,channel);
-                restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if ((!dev.chanlock) ||
+	    ((dev.channel == channel) && (dev.mcard == card))) {
+		dev.chanlock++;
+		icn_map_channel(card, channel);
+		restore_flags(flags);
 #ifdef MAP_DEBUG
-                printk(KERN_DEBUG "trymaplock %d OK\n", channel);
+		printk(KERN_DEBUG "trymaplock %d OK\n", channel);
 #endif
-                return 1;
-        }
-        restore_flags(flags);
+		return 1;
+	}
+	restore_flags(flags);
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
+	printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
 #endif
-        return 0;
+	return 0;
 }
 
 /*
  * Release current card/channel lock,
  * then map same or other channel without locking.
  */
-static inline void icn_maprelease_channel(icn_card *card, int channel)
+static inline void
+icn_maprelease_channel(icn_card * card, int channel)
 {
-        ulong flags;
+	ulong flags;
 
-        save_flags(flags);
-        cli();
 #ifdef MAP_DEBUG
-        printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);
+	printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);
 #endif
-        if (dev.chanlock)
-                dev.chanlock--;
-        if (!dev.chanlock)
-                icn_map_channel(card,channel);
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (dev.chanlock > 0)
+		dev.chanlock--;
+	if (!dev.chanlock)
+		icn_map_channel(card, channel);
+	restore_flags(flags);
 }
 
 /* Get Data from the B-Channel, assemble fragmented packets and put them
@@ -323,47 +358,48 @@
  * This routine is called via timer-callback from icn_pollbchan().
  */
 
-static void icn_pollbchan_receive(int channel, icn_card *card)
+static void
+icn_pollbchan_receive(int channel, icn_card * card)
 {
-        int mch = channel + ((card->secondhalf) ? 2 : 0);
-        int eflag;
-        int cnt;
+	int mch = channel + ((card->secondhalf) ? 2 : 0);
+	int eflag;
+	int cnt;
 	struct sk_buff *skb;
 
-        if (icn_trymaplock_channel(card,mch)) {
-                while (rbavl) {
-                        cnt = readb(&rbuf_l);
-                        if ((card->rcvidx[channel] + cnt) > 4000) {
-                                printk(KERN_WARNING
-                                       "icn: (%s) bogus packet on ch%d, dropping.\n",
-                                       CID,
-                                       channel + 1);
-                                card->rcvidx[channel] = 0;
-                                eflag = 0;
-                        } else {
-                                memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],
-                                              &rbuf_d, cnt);
-                                card->rcvidx[channel] += cnt;
-                                eflag = readb(&rbuf_f);
-                        }
-                        rbnext;
-                        icn_maprelease_channel(card, mch & 2);
-                        if (!eflag) {
-                                if ((cnt = card->rcvidx[channel])) {
-                                        if (!(skb = dev_alloc_skb(cnt))) {
-                                                printk(KERN_WARNING "ïcn: receive out of memory\n");
-                                                break;
-                                        }
-                                        memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt);
-                                        card->rcvidx[channel] = 0;
-                                        card->interface.rcvcallb_skb(card->myid, channel, skb);
-                                }
-                        }
-                        if (!icn_trymaplock_channel(card, mch))
-                                break;
-                }
-                icn_maprelease_channel(card, mch & 2);
-        }
+	if (icn_trymaplock_channel(card, mch)) {
+		while (rbavl) {
+			cnt = readb(&rbuf_l);
+			if ((card->rcvidx[channel] + cnt) > 4000) {
+				printk(KERN_WARNING
+				       "icn: (%s) bogus packet on ch%d, dropping.\n",
+				       CID,
+				       channel + 1);
+				card->rcvidx[channel] = 0;
+				eflag = 0;
+			} else {
+				memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],
+					      &rbuf_d, cnt);
+				card->rcvidx[channel] += cnt;
+				eflag = readb(&rbuf_f);
+			}
+			rbnext;
+			icn_maprelease_channel(card, mch & 2);
+			if (!eflag) {
+				if ((cnt = card->rcvidx[channel])) {
+					if (!(skb = dev_alloc_skb(cnt))) {
+						printk(KERN_WARNING "ïcn: receive out of memory\n");
+						break;
+					}
+					memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt);
+					card->rcvidx[channel] = 0;
+					card->interface.rcvcallb_skb(card->myid, channel, skb);
+				}
+			}
+			if (!icn_trymaplock_channel(card, mch))
+				break;
+		}
+		icn_maprelease_channel(card, mch & 2);
+	}
 }
 
 /* Send data-packet to B-Channel, split it up into fragments of
@@ -373,56 +409,59 @@
  * directly from icn_sendbuf().
  */
 
-static void icn_pollbchan_send(int channel, icn_card *card)
+static void
+icn_pollbchan_send(int channel, icn_card * card)
 {
-        int mch = channel + ((card->secondhalf) ? 2 : 0);
-        int cnt;
+	int mch = channel + ((card->secondhalf) ? 2 : 0);
+	int cnt;
 	unsigned long flags;
-        struct sk_buff *skb;
-        isdn_ctrl cmd;
+	struct sk_buff *skb;
+	isdn_ctrl cmd;
 
-        if (!card->sndcount[channel])
-                return;
-        if (icn_trymaplock_channel(card,mch)) {
-                while (sbfree && card->sndcount[channel]) {
-                        save_flags(flags);
-                        cli();
-                        if (card->xmit_lock[channel]) {
-                                restore_flags(flags);
-                                break;
-                        }
-                        card->xmit_lock[channel]++;
-                        restore_flags(flags);
-                        skb = skb_dequeue(&card->spqueue[channel]);
-                        if (!skb)
-                                break;
-		        if (skb->len > ICN_FRAGSIZE) {
-			    writeb (0xff, &sbuf_f);
-			    cnt = ICN_FRAGSIZE;
+	if (!(card->sndcount[channel] ||
+	      skb_queue_len(&card->spqueue[channel])))
+		return;
+	if (icn_trymaplock_channel(card, mch)) {
+		while (sbfree && (card->sndcount[channel] ||
+			       skb_queue_len(&card->spqueue[channel]))) {
+			save_flags(flags);
+			cli();
+			if (card->xmit_lock[channel]) {
+				restore_flags(flags);
+				break;
+			}
+			card->xmit_lock[channel]++;
+			restore_flags(flags);
+			skb = skb_dequeue(&card->spqueue[channel]);
+			if (!skb)
+				break;
+			if (skb->len > ICN_FRAGSIZE) {
+				writeb(0xff, &sbuf_f);
+				cnt = ICN_FRAGSIZE;
 			} else {
-			    writeb (0x0, &sbuf_f);
-			    cnt = skb->len;
+				writeb(0x0, &sbuf_f);
+				cnt = skb->len;
 			}
-		        writeb (cnt, &sbuf_l);
-                        memcpy_toio(&sbuf_d, skb->data, cnt);
-                        skb_pull(skb, cnt);
-                        card->sndcount[channel] -= cnt;
-                        sbnext;        /* switch to next buffer        */
-                        icn_maprelease_channel(card, mch & 2);
-                        if (!skb->len) {
-                                dev_kfree_skb(skb, FREE_WRITE);
-                                cmd.command = ISDN_STAT_BSENT;
-                                cmd.driver = card->myid;
-                                cmd.arg = channel;
-                                card->interface.statcallb(&cmd);
-                        } else
-                                skb_queue_head(&card->spqueue[channel], skb);
-                        card->xmit_lock[channel] = 0;
-                        if (!icn_trymaplock_channel(card, mch))
-                                break;
-                }
-                icn_maprelease_channel(card, mch & 2);
-        }
+			writeb(cnt, &sbuf_l);
+			memcpy_toio(&sbuf_d, skb->data, cnt);
+			skb_pull(skb, cnt);
+			card->sndcount[channel] -= cnt;
+			sbnext; /* switch to next buffer        */
+			icn_maprelease_channel(card, mch & 2);
+			if (!skb->len) {
+				dev_kfree_skb(skb, FREE_WRITE);
+				cmd.command = ISDN_STAT_BSENT;
+				cmd.driver = card->myid;
+				cmd.arg = channel;
+				card->interface.statcallb(&cmd);
+			} else
+				skb_queue_head(&card->spqueue[channel], skb);
+			card->xmit_lock[channel] = 0;
+			if (!icn_trymaplock_channel(card, mch))
+				break;
+		}
+		icn_maprelease_channel(card, mch & 2);
+	}
 }
 
 /* Send/Receive Data to/from the B-Channel.
@@ -430,54 +469,59 @@
  * It schedules itself while any B-Channel is open.
  */
 
-static void icn_pollbchan(unsigned long data)
+static void
+icn_pollbchan(unsigned long data)
 {
-        icn_card *card = (icn_card *)data;
-        unsigned long flags;
+	icn_card *card = (icn_card *) data;
+	unsigned long flags;
 
-                if (card->flags & ICN_FLAGS_B1ACTIVE) {
-                        icn_pollbchan_receive(0, card);
-                        icn_pollbchan_send(0, card);
-                }
-                if (card->flags & ICN_FLAGS_B2ACTIVE) {
-                        icn_pollbchan_receive(1, card);
-                        icn_pollbchan_send(1, card);
-                }
-                if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
-                        /* schedule b-channel polling again */
-                        save_flags(flags);
-                        cli();
-                        del_timer(&card->rb_timer);
-                        card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
-                        add_timer(&card->rb_timer);
-                        card->flags |= ICN_FLAGS_RBTIMER;
-                        restore_flags(flags);
-                } else
-                        card->flags &= ~ICN_FLAGS_RBTIMER;
+	if (card->flags & ICN_FLAGS_B1ACTIVE) {
+		icn_pollbchan_receive(0, card);
+		icn_pollbchan_send(0, card);
+	}
+	if (card->flags & ICN_FLAGS_B2ACTIVE) {
+		icn_pollbchan_receive(1, card);
+		icn_pollbchan_send(1, card);
+	}
+	if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+		/* schedule b-channel polling again */
+		save_flags(flags);
+		cli();
+		del_timer(&card->rb_timer);
+		card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+		add_timer(&card->rb_timer);
+		card->flags |= ICN_FLAGS_RBTIMER;
+		restore_flags(flags);
+	} else
+		card->flags &= ~ICN_FLAGS_RBTIMER;
 }
 
 typedef struct icn_stat {
-        char *statstr;
-        int  command;
-        int  action;
+	char *statstr;
+	int command;
+	int action;
 } icn_stat;
 
-static icn_stat icn_stat_table[] = {
-        {"BCON_",          ISDN_STAT_BCONN, 1}, /* B-Channel connected        */
-        {"BDIS_",          ISDN_STAT_BHUP,  2}, /* B-Channel disconnected     */
-        {"DCON_",          ISDN_STAT_DCONN, 0}, /* D-Channel connected        */
-        {"DDIS_",          ISDN_STAT_DHUP,  0}, /* D-Channel disconnected     */
-        {"DCAL_I",         ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line  */
-        {"DSCA_I",         ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV     */
-        {"FCALL",          ISDN_STAT_ICALL, 4}, /* Leased line connection up  */
-        {"CIF",            ISDN_STAT_CINF,  5}, /* Charge-info, 1TR6-type     */
-        {"AOC",            ISDN_STAT_CINF,  6}, /* Charge-info, DSS1-type     */
-        {"CAU",            ISDN_STAT_CAUSE, 7}, /* Cause code                 */
-        {"TEI OK",         ISDN_STAT_RUN,   0}, /* Card connected to wallplug */
-        {"NO D-CHAN",      ISDN_STAT_NODCH, 0}, /* No D-channel available     */
-        {"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8}, /* Layer-1 activation failed  */
-        {NULL,             0             , -1}
+/* *INDENT-OFF* */
+static icn_stat icn_stat_table[] =
+{
+	{"BCON_",          ISDN_STAT_BCONN, 1},	/* B-Channel connected        */
+	{"BDIS_",          ISDN_STAT_BHUP,  2},	/* B-Channel disconnected     */
+	{"DCON_",          ISDN_STAT_DCONN, 0},	/* D-Channel connected        */
+	{"DDIS_",          ISDN_STAT_DHUP,  0},	/* D-Channel disconnected     */
+	{"DCAL_I",         ISDN_STAT_ICALL, 3},	/* Incoming call dialup-line  */
+	{"DSCA_I",         ISDN_STAT_ICALL, 3},	/* Incoming call 1TR6-SPV     */
+	{"FCALL",          ISDN_STAT_ICALL, 4},	/* Leased line connection up  */
+	{"CIF",            ISDN_STAT_CINF,  5},	/* Charge-info, 1TR6-type     */
+	{"AOC",            ISDN_STAT_CINF,  6},	/* Charge-info, DSS1-type     */
+	{"CAU",            ISDN_STAT_CAUSE, 7},	/* Cause code                 */
+	{"TEI OK",         ISDN_STAT_RUN,   0},	/* Card connected to wallplug */
+	{"NO D-CHAN",      ISDN_STAT_NODCH, 0},	/* No D-channel available     */
+	{"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8},	/* Layer-1 activation failed  */
+	{"E_L2: DATA LIN", ISDN_STAT_BHUP,  8},	/* Layer-2 data link lost     */
+	{NULL, 0, -1}
 };
+/* *INDENT-ON* */
 
 /*
  * Check Statusqueue-Pointer from isdn-cards.
@@ -489,192 +533,223 @@
  * This routine is called periodically via timer.
  */
 
-static int icn_parse_status(u_char *status, int channel, icn_card *card)
+static int
+icn_parse_status(u_char * status, int channel, icn_card * card)
 {
-        icn_stat  *s = icn_stat_table;
-        int       action = -1;
-        int       dflag  = 0;
-        unsigned long flags;
-        isdn_ctrl cmd;
-
-        while (s->statstr) {
-                if (!strncmp(status,s->statstr,strlen(s->statstr))) {
-                        cmd.command = s->command;
-                        action = s->action;
-                        break;
-                }
-                s++;
-        }
-        if (action==-1)
-                return 0;
-        cmd.driver = card->myid;
-        cmd.arg = channel;
-        switch (action) {
-                case 1:
-                        card->flags |= (channel)?
-                                ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE;
-                        break;
-                case 2:
-                        card->flags &= ~((channel)?
-                                ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
-                        icn_free_queue(&card->spqueue[channel]);
-                        save_flags(flags);
-                        cli();
-                        card->rcvidx[channel] = 0;
-                        restore_flags(flags);
-                        dflag |= (channel+1);
-                        break;
-                case 3:
-                        strncpy(cmd.num, status + 6, sizeof(cmd.num) - 1);
-                        break;
-                case 4:
-                        sprintf(cmd.num,"LEASED%d,07,00,%d",
-                                 card->myid,channel+1);
-                        break;
-                case 5:
-                        strncpy(cmd.num, status + 3, sizeof(cmd.num) - 1);
-                        break;
-                case 6:
-                        sprintf(cmd.num,"%d",
-                                (int)simple_strtoul(status + 7,NULL,16));
-                        break;
-                case 7:
-                        status += 3;
-                        if (strlen(status)==4)
-                                sprintf(cmd.num,"%s%c%c",
-                                        status+2,*status,*(status+1));
-                        else
-                                strncpy(cmd.num, status+1, sizeof(cmd.num) - 1);
-                        break;
-                case 8:
-                        cmd.arg = 0;
-                        cmd.driver = card->myid;
-                        card->interface.statcallb(&cmd);
-                        cmd.command = ISDN_STAT_DHUP;
-                        cmd.arg = 0;
-                        cmd.driver = card->myid;
-                        card->interface.statcallb(&cmd);
-                        cmd.command = ISDN_STAT_BHUP;
-                        cmd.arg = 1;
-                        cmd.driver = card->myid;
-                        card->interface.statcallb(&cmd);
-                        cmd.command = ISDN_STAT_DHUP;
-                        cmd.arg = 1;
-                        cmd.driver = card->myid;
-                        break;
-        }
-        card->interface.statcallb(&cmd);
-        return dflag;
-}
-
-static void icn_polldchan(unsigned long data)
-{
-        icn_card *card = (icn_card *)data;
-        int mch = card->secondhalf ? 2 : 0;
-        int avail = 0;
-        int dflag = 0;
-        int left;
-        u_char c;
-        int ch;
-        int flags;
-        int i;
-        u_char *p;
-        isdn_ctrl cmd;
-
-        if (icn_trymaplock_channel(card,mch)) {
-                avail = msg_avail;
-                for (left = avail, i = readb(&msg_o); left > 0; i++, left--) {
-                        c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]);
-                        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);
-                        if (c == 0xff) {
-                                card->imsg[card->iptr] = 0;
-                                card->iptr = 0;
-                                if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
-                                    card->imsg[1] <= '2' && card->imsg[2] == ';') {
-                                        ch = (card->imsg[1] - '0') - 1;
-                                        p = &card->imsg[3];
-                                        dflag |= icn_parse_status(p, ch, card);
-                                } else {
-                                        p = card->imsg;
-                                        if (!strncmp(p, "DRV1.", 5)) {
-                                                u_char vstr[10];
-                                                u_char *q = vstr;
-
-                                                printk(KERN_INFO "icn: (%s) %s\n",CID,p);
-                                                if (!strncmp(p + 7, "TC", 2)) {
-                                                        card->ptype = ISDN_PTYPE_1TR6;
-                                                        card->interface.features |= ISDN_FEATURE_P_1TR6;
-                                                        printk(KERN_INFO
-                                                               "icn: (%s) 1TR6-Protocol loaded and running\n",CID);
-                                                }
-                                                if (!strncmp(p + 7, "EC", 2)) {
-                                                        card->ptype = ISDN_PTYPE_EURO;
-                                                        card->interface.features |= ISDN_FEATURE_P_EURO;
-                                                        printk(KERN_INFO
-                                                               "icn: (%s) Euro-Protocol loaded and running\n",CID);
-                                                }
-                                                p = strstr(card->imsg,"BRV") + 3;
-                                                while (*p) {
-                                                        if (*p>='0' && *p<='9')
-                                                                *q++ = *p;
-                                                        p++;
-                                                }
-                                                *q = '\0';
-                                                strcat(vstr,"000");
-                                                vstr[3] = '\0';
-                                                card->fw_rev = (int)simple_strtoul(vstr,NULL,10);
-                                                continue;
-
-                                        }
-                                }
-                        } else {
-                                card->imsg[card->iptr] = c;
-                                if (card->iptr < 59)
-                                        card->iptr++;
-                        }
-                }
-                writeb((readb(&msg_o) + avail) & 0xff, &msg_o);
-                icn_release_channel();
-        }
-        if (avail) {
-                cmd.command = ISDN_STAT_STAVAIL;
-                cmd.driver = card->myid;
-                cmd.arg = avail;
-                card->interface.statcallb(&cmd);
-        }
-        if (dflag & 1)
-                card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0);
-        if (dflag & 2)
-                card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0);
-        if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
-                if (!(card->flags & ICN_FLAGS_RBTIMER)) {
-                        /* schedule b-channel polling */
-                        card->flags |= ICN_FLAGS_RBTIMER;
-                        save_flags(flags);
-                        cli();
-                        del_timer(&card->rb_timer);
-                        card->rb_timer.function = icn_pollbchan;
-                        card->rb_timer.data = (unsigned long)card;
-                        card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
-                        add_timer(&card->rb_timer);
-                        restore_flags(flags);
-                }
-        /* schedule again */
-        save_flags(flags);
-        cli();
-        del_timer(&card->st_timer);
-        card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
-        add_timer(&card->st_timer);
-        restore_flags(flags);
+	icn_stat *s = icn_stat_table;
+	int action = -1;
+	int dflag = 0;
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	while (s->statstr) {
+		if (!strncmp(status, s->statstr, strlen(s->statstr))) {
+			cmd.command = s->command;
+			action = s->action;
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return 0;
+	cmd.driver = card->myid;
+	cmd.arg = channel;
+	switch (action) {
+		case 1:
+			card->flags |= (channel) ?
+			    ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;
+			break;
+		case 2:
+			card->flags &= ~((channel) ?
+				ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE);
+			icn_free_queue(&card->spqueue[channel]);
+			save_flags(flags);
+			cli();
+			card->rcvidx[channel] = 0;
+			restore_flags(flags);
+			dflag |= (channel + 1);
+			break;
+		case 3:
+			{
+				char *s = strtok(status + 6, ",");
+				strncpy(cmd.parm.setup.phone, s, sizeof(cmd.parm.setup.phone));
+				s = strtok(NULL, ",");
+				if (!strlen(s))
+					cmd.parm.setup.si1 = 0;
+				else
+					cmd.parm.setup.si1 = simple_strtoul(s, NULL, 10);
+				s = strtok(NULL, ",");
+				if (!strlen(s))
+					cmd.parm.setup.si2 = 0;
+				else
+					cmd.parm.setup.si2 = simple_strtoul(s, NULL, 10);
+				s = strtok(NULL, ",");
+				strncpy(cmd.parm.setup.eazmsn, s, sizeof(cmd.parm.setup.eazmsn));
+			}
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 4:
+			sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);
+			sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);
+			cmd.parm.setup.si1 = 7;
+			cmd.parm.setup.si2 = 0;
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 5:
+			strncpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num) - 1);
+			break;
+		case 6:
+			sprintf(cmd.parm.num, "%d",
+			     (int) simple_strtoul(status + 7, NULL, 16));
+			break;
+		case 7:
+			status += 3;
+			if (strlen(status) == 4)
+				sprintf(cmd.parm.num, "%s%c%c",
+				     status + 2, *status, *(status + 1));
+			else
+				strncpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num) - 1);
+			break;
+		case 8:
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_BHUP;
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			break;
+	}
+	card->interface.statcallb(&cmd);
+	return dflag;
+}
+
+static void
+icn_putmsg(icn_card * card, unsigned char c)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	*card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+	if (card->msg_buf_write == card->msg_buf_read) {
+		if (++card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	if (card->msg_buf_write > card->msg_buf_end)
+		card->msg_buf_write = card->msg_buf;
+	restore_flags(flags);
+}
+
+static void
+icn_polldchan(unsigned long data)
+{
+	icn_card *card = (icn_card *) data;
+	int mch = card->secondhalf ? 2 : 0;
+	int avail = 0;
+	int dflag = 0;
+	int left;
+	u_char c;
+	int ch;
+	int flags;
+	int i;
+	u_char *p;
+	isdn_ctrl cmd;
+
+	if (icn_trymaplock_channel(card, mch)) {
+		avail = msg_avail;
+		for (left = avail, i = readb(&msg_o); left > 0; i++, left--) {
+			c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]);
+			icn_putmsg(card, c);
+			if (c == 0xff) {
+				card->imsg[card->iptr] = 0;
+				card->iptr = 0;
+				if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+				    card->imsg[1] <= '2' && card->imsg[2] == ';') {
+					ch = (card->imsg[1] - '0') - 1;
+					p = &card->imsg[3];
+					dflag |= icn_parse_status(p, ch, card);
+				} else {
+					p = card->imsg;
+					if (!strncmp(p, "DRV1.", 5)) {
+						u_char vstr[10];
+						u_char *q = vstr;
+
+						printk(KERN_INFO "icn: (%s) %s\n", CID, p);
+						if (!strncmp(p + 7, "TC", 2)) {
+							card->ptype = ISDN_PTYPE_1TR6;
+							card->interface.features |= ISDN_FEATURE_P_1TR6;
+							printk(KERN_INFO
+							       "icn: (%s) 1TR6-Protocol loaded and running\n", CID);
+						}
+						if (!strncmp(p + 7, "EC", 2)) {
+							card->ptype = ISDN_PTYPE_EURO;
+							card->interface.features |= ISDN_FEATURE_P_EURO;
+							printk(KERN_INFO
+							       "icn: (%s) Euro-Protocol loaded and running\n", CID);
+						}
+						p = strstr(card->imsg, "BRV") + 3;
+						while (*p) {
+							if (*p >= '0' && *p <= '9')
+								*q++ = *p;
+							p++;
+						}
+						*q = '\0';
+						strcat(vstr, "000");
+						vstr[3] = '\0';
+						card->fw_rev = (int) simple_strtoul(vstr, NULL, 10);
+						continue;
+
+					}
+				}
+			} else {
+				card->imsg[card->iptr] = c;
+				if (card->iptr < 59)
+					card->iptr++;
+			}
+		}
+		writeb((readb(&msg_o) + avail) & 0xff, &msg_o);
+		icn_release_channel();
+	}
+	if (avail) {
+		cmd.command = ISDN_STAT_STAVAIL;
+		cmd.driver = card->myid;
+		cmd.arg = avail;
+		card->interface.statcallb(&cmd);
+	}
+	if (dflag & 1)
+		card->interface.rcvcallb(card->myid, 0, card->rcvbuf[0], 0);
+	if (dflag & 2)
+		card->interface.rcvcallb(card->myid, 1, card->rcvbuf[1], 0);
+	if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
+		if (!(card->flags & ICN_FLAGS_RBTIMER)) {
+			/* schedule b-channel polling */
+			card->flags |= ICN_FLAGS_RBTIMER;
+			save_flags(flags);
+			cli();
+			del_timer(&card->rb_timer);
+			card->rb_timer.function = icn_pollbchan;
+			card->rb_timer.data = (unsigned long) card;
+			card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+			add_timer(&card->rb_timer);
+			restore_flags(flags);
+		}
+	/* schedule again */
+	save_flags(flags);
+	cli();
+	del_timer(&card->st_timer);
+	card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+	add_timer(&card->st_timer);
+	restore_flags(flags);
 }
 
 /* Append a packet to the transmit buffer-queue.
@@ -686,34 +761,35 @@
  *   Number of bytes transferred, -E??? on error
  */
 
-static int icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card)
+static int
+icn_sendbuf(int channel, struct sk_buff *skb, icn_card * card)
 {
-        int len = skb->len;
-        unsigned long flags;
-        struct sk_buff *nskb;
-
-        if (len > 4000) {
-                printk(KERN_WARNING
-                       "icn: Send packet too large\n");
-                return -EINVAL;
-        }
-        if (len) {
-                if (!(card->flags & (channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE))
-                        return 0;
-                if (card->sndcount[channel] > ICN_MAX_SQUEUE)
-                        return 0;
-                save_flags(flags);
-                cli();
-                nskb = skb_clone(skb, GFP_ATOMIC);
-                if (nskb) {
-                        skb_queue_tail(&card->spqueue[channel], nskb);
-                        dev_kfree_skb(skb, FREE_WRITE);
-                } else
-                        len = 0;
-                card->sndcount[channel] += len;
-                restore_flags(flags);
-        }
-        return len;
+	int len = skb->len;
+	unsigned long flags;
+	struct sk_buff *nskb;
+
+	if (len > 4000) {
+		printk(KERN_WARNING
+		       "icn: Send packet too large\n");
+		return -EINVAL;
+	}
+	if (len) {
+		if (!(card->flags & (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE))
+			return 0;
+		if (card->sndcount[channel] > ICN_MAX_SQUEUE)
+			return 0;
+		save_flags(flags);
+		cli();
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			skb_queue_tail(&card->spqueue[channel], nskb);
+			dev_kfree_skb(skb, FREE_WRITE);
+		} else
+			len = 0;
+		card->sndcount[channel] += len;
+		restore_flags(flags);
+	}
+	return len;
 }
 
 /*
@@ -723,37 +799,38 @@
  *   0 on success (Boot loader ready)
  *   -EIO on failure (timeout)
  */
-static int icn_check_loader(int cardnumber)
+static int
+icn_check_loader(int cardnumber)
 {
-        int timer = 0;
+	int timer = 0;
 
-        while (1) {
+	while (1) {
 #ifdef BOOT_DEBUG
-                printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
+		printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
 #endif
-                if (readb(&dev.shmem->data_control.scns) ||
-                    readb(&dev.shmem->data_control.scnr)) {
-                        if (timer++ > 5) {
-                                printk(KERN_WARNING
-                                       "icn: Boot-Loader %d timed out.\n",
-                                       cardnumber);
-                                icn_release_channel();
-                                return -EIO;
-                        }
+		if (readb(&dev.shmem->data_control.scns) ||
+		    readb(&dev.shmem->data_control.scnr)) {
+			if (timer++ > 5) {
+				printk(KERN_WARNING
+				       "icn: Boot-Loader %d timed out.\n",
+				       cardnumber);
+				icn_release_channel();
+				return -EIO;
+			}
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
+			printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
 #endif
-                        current->state = TASK_INTERRUPTIBLE;
-                        current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                        schedule();
-                } else {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+			schedule();
+		} else {
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
+			printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
 #endif
-                        icn_release_channel();
-                        return 0;
-                }
-        }
+			icn_release_channel();
+			return 0;
+		}
+	}
 }
 
 /* Load the boot-code into the interface-card's memory and start it.
@@ -780,894 +857,935 @@
 #define SLEEP(sec)
 #endif
 
-static int icn_loadboot(u_char * buffer, icn_card * card)
+static int
+icn_loadboot(u_char * buffer, icn_card * card)
 {
-        int ret;
-        ulong flags;
+	int ret;
+	ulong flags;
 	u_char *codebuf;
 
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
+	printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
 #endif
-        if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE1)))
-                return ret;
-        if (!(codebuf = kmalloc(ICN_CODE_STAGE1,GFP_KERNEL))) {
-                printk(KERN_WARNING "icn: Could not allocate code buffer\n");
-                return -ENOMEM;
-        }
-        save_flags(flags);
-        cli();
-        if (!card->rvalid) {
-                if (check_region(card->port, ICN_PORTLEN)) {
-                        printk(KERN_WARNING
-                               "icn: (%s) ports 0x%03x-0x%03x in use.\n",
-                               CID,
-                               card->port,
-                               card->port + ICN_PORTLEN);
-                        restore_flags(flags);
-                        kfree(codebuf);
-                        return -EBUSY;
-                }
-                request_region(card->port, ICN_PORTLEN, card->regname);
-                card->rvalid = 1;
-                if (card->doubleS0)
-                        card->other->rvalid = 1;
-        }
-        if (!dev.mvalid) {
-                if (check_shmem((ulong) dev.shmem, 0x4000)) {
-                        printk(KERN_WARNING
-                               "icn: memory at 0x%08lx in use.\n",
-                               (ulong) dev.shmem);
-                        restore_flags(flags);
-                        return -EBUSY;
-                }
-                request_shmem((ulong) dev.shmem, 0x4000, "icn");
-                dev.mvalid = 1;
-        }
-        restore_flags(flags);
-        OUTB_P(0, ICN_RUN);                                       /* Reset Controller */
-        OUTB_P(0, ICN_MAPRAM);                                    /* Disable RAM      */
-        icn_shiftout(ICN_CFG, 0x0f, 3, 4);                        /* Windowsize= 16k  */
-        icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10); /* Set RAM-Addr.    */
+	if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE1)))
+		return ret;
+	if (!(codebuf = kmalloc(ICN_CODE_STAGE1, GFP_KERNEL))) {
+		printk(KERN_WARNING "icn: Could not allocate code buffer\n");
+		return -ENOMEM;
+	}
+	save_flags(flags);
+	cli();
+	if (!card->rvalid) {
+		if (check_region(card->port, ICN_PORTLEN)) {
+			printk(KERN_WARNING
+			       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+			       CID,
+			       card->port,
+			       card->port + ICN_PORTLEN);
+			restore_flags(flags);
+			kfree(codebuf);
+			return -EBUSY;
+		}
+		request_region(card->port, ICN_PORTLEN, card->regname);
+		card->rvalid = 1;
+		if (card->doubleS0)
+			card->other->rvalid = 1;
+	}
+	if (!dev.mvalid) {
+		if (check_shmem((ulong) dev.shmem, 0x4000)) {
+			printk(KERN_WARNING
+			       "icn: memory at 0x%08lx in use.\n",
+			       (ulong) dev.shmem);
+			restore_flags(flags);
+			return -EBUSY;
+		}
+		request_shmem((ulong) dev.shmem, 0x4000, "icn");
+		dev.mvalid = 1;
+	}
+	restore_flags(flags);
+	OUTB_P(0, ICN_RUN);     /* Reset Controller */
+	OUTB_P(0, ICN_MAPRAM);  /* Disable RAM      */
+	icn_shiftout(ICN_CFG, 0x0f, 3, 4);	/* Windowsize= 16k  */
+	icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10);	/* Set RAM-Addr.    */
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem);
+	printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem);
 #endif
-        SLEEP(1);
-        save_flags(flags);
-        cli();
+	SLEEP(1);
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "Map Bank 0\n");
+	printk(KERN_DEBUG "Map Bank 0\n");
 #endif
-        icn_map_channel(card,0);                                   /* Select Bank 0    */
-        icn_lock_channel(card,0);                                  /* Lock Bank 0      */
-        restore_flags(flags);
-        SLEEP(1);
-        if (copy_from_user(codebuf, buffer, ICN_CODE_STAGE1))
-        	return -EFAULT;
-        memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);           /* Copy code        */
+	save_flags(flags);
+	cli();
+	icn_map_channel(card, 0);	/* Select Bank 0    */
+	icn_lock_channel(card, 0);	/* Lock Bank 0      */
+	restore_flags(flags);
+	SLEEP(1);
+	copy_from_user(codebuf, buffer, ICN_CODE_STAGE1);
+	memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);	/* Copy code        */
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "Bootloader transfered\n");
+	printk(KERN_DEBUG "Bootloader transfered\n");
 #endif
-        if (card->doubleS0) {
-                SLEEP(1);
-                save_flags(flags);
-                cli();
-                icn_release_channel();
+	if (card->doubleS0) {
+		SLEEP(1);
 #ifdef BOOT_DEBUG
-                printk(KERN_DEBUG "Map Bank 8\n");
+		printk(KERN_DEBUG "Map Bank 8\n");
 #endif
-                icn_map_channel(card,2);                           /* Select Bank 8   */
-                icn_lock_channel(card,2);                          /* Lock Bank 8     */
-                restore_flags(flags);
-                SLEEP(1);
-                memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);           /* Copy code        */
+		save_flags(flags);
+		cli();
+		icn_release_channel();
+		icn_map_channel(card, 2);	/* Select Bank 8   */
+		icn_lock_channel(card, 2);	/* Lock Bank 8     */
+		restore_flags(flags);
+		SLEEP(1);
+		memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);	/* Copy code        */
 #ifdef BOOT_DEBUG
-                printk(KERN_DEBUG "Bootloader transfered\n");
+		printk(KERN_DEBUG "Bootloader transfered\n");
 #endif
-        }
-        kfree(codebuf);
-        SLEEP(1);
-        OUTB_P(0xff, ICN_RUN);                                     /* Start Boot-Code */
-        if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1)))
-                return ret;
-        if (!card->doubleS0)
-                return 0;
-        /* reached only, if we have a Double-S0-Card */
-        save_flags(flags);
-        cli();
+	}
+	kfree(codebuf);
+	SLEEP(1);
+	OUTB_P(0xff, ICN_RUN);  /* Start Boot-Code */
+	if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1)))
+		return ret;
+	if (!card->doubleS0)
+		return 0;
+	/* reached only, if we have a Double-S0-Card */
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "Map Bank 0\n");
+	printk(KERN_DEBUG "Map Bank 0\n");
 #endif
-        icn_map_channel(card,0);                                   /* Select Bank 0   */
-        icn_lock_channel(card,0);                                  /* Lock Bank 0     */
-        restore_flags(flags);
-        SLEEP(1);
-        return (icn_check_loader(1));
-}
-
-static int icn_loadproto(u_char * buffer, icn_card * card)
-{
-        register u_char *p = buffer;
-        u_char codebuf[256];
-        uint left = ICN_CODE_STAGE2;
-        uint cnt;
-        int timer;
-        int ret;
-        unsigned long flags;
+	save_flags(flags);
+	cli();
+	icn_map_channel(card, 0);	/* Select Bank 0   */
+	icn_lock_channel(card, 0);	/* Lock Bank 0     */
+	restore_flags(flags);
+	SLEEP(1);
+	return (icn_check_loader(1));
+}
+
+static int
+icn_loadproto(u_char * buffer, icn_card * card)
+{
+	register u_char *p = buffer;
+	u_char codebuf[256];
+	uint left = ICN_CODE_STAGE2;
+	uint cnt;
+	int timer;
+	int ret;
+	unsigned long flags;
 
 #ifdef BOOT_DEBUG
-        printk(KERN_DEBUG "icn_loadproto called\n");
+	printk(KERN_DEBUG "icn_loadproto called\n");
 #endif
-        if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2)))
-                return ret;
-        timer = 0;
-        save_flags(flags);
-        cli();
-        if (card->secondhalf) {
-                icn_map_channel(card, 2);
-                icn_lock_channel(card, 2);
-        } else {
-                icn_map_channel(card, 0);
-                icn_lock_channel(card, 0);
-        }
-        restore_flags(flags);
-        while (left) {
-                if (sbfree) {                           /* If there is a free buffer...  */
-                        cnt = MIN(256, left);
-                        if (copy_from_user(codebuf, p, cnt))
-                        	/* FIXME -WRONG */return -EFAULT;
-                        memcpy_toio(&sbuf_l, codebuf, cnt); /* copy data                     */
-                        sbnext;                         /* switch to next buffer         */
-                        p += cnt;
-                        left -= cnt;
-                        timer = 0;
-                } else {
+	if ((ret = verify_area(VERIFY_READ, (void *) buffer, ICN_CODE_STAGE2)))
+		return ret;
+	timer = 0;
+	save_flags(flags);
+	cli();
+	if (card->secondhalf) {
+		icn_map_channel(card, 2);
+		icn_lock_channel(card, 2);
+	} else {
+		icn_map_channel(card, 0);
+		icn_lock_channel(card, 0);
+	}
+	restore_flags(flags);
+	while (left) {
+		if (sbfree) {   /* If there is a free buffer...  */
+			cnt = MIN(256, left);
+			copy_from_user(codebuf, p, cnt);
+			memcpy_toio(&sbuf_l, codebuf, cnt);	/* copy data                     */
+			sbnext; /* switch to next buffer         */
+			p += cnt;
+			left -= cnt;
+			timer = 0;
+		} else {
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "boot 2 !sbfree\n");
+			printk(KERN_DEBUG "boot 2 !sbfree\n");
 #endif
-                        if (timer++ > 5) {
-                                icn_maprelease_channel(card, 0);
-                                return -EIO;
-                        }
-                        current->state = TASK_INTERRUPTIBLE;
-                        current->timeout = jiffies + 10;
-                        schedule();
-                }
-        }
-        writeb (0x20, &sbuf_n);
-        timer = 0;
-        while (1) {
-                if (readb(&cmd_o) || readb(&cmd_i)) {
+			if (timer++ > 5) {
+				icn_maprelease_channel(card, 0);
+				return -EIO;
+			}
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 10;
+			schedule();
+		}
+	}
+	writeb(0x20, &sbuf_n);
+	timer = 0;
+	while (1) {
+		if (readb(&cmd_o) || readb(&cmd_i)) {
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Proto?\n");
+			printk(KERN_DEBUG "Proto?\n");
 #endif
-                        if (timer++ > 5) {
-                                printk(KERN_WARNING
-                                       "icn: (%s) Protocol timed out.\n",
-                                       CID);
+			if (timer++ > 5) {
+				printk(KERN_WARNING
+				       "icn: (%s) Protocol timed out.\n",
+				       CID);
 #ifdef BOOT_DEBUG
-                                printk(KERN_DEBUG "Proto TO!\n");
+				printk(KERN_DEBUG "Proto TO!\n");
 #endif
-                                icn_maprelease_channel(card, 0);
-                                return -EIO;
-                        }
+				icn_maprelease_channel(card, 0);
+				return -EIO;
+			}
 #ifdef BOOT_DEBUG
-                        printk(KERN_DEBUG "Proto TO?\n");
+			printk(KERN_DEBUG "Proto TO?\n");
 #endif
-                        current->state = TASK_INTERRUPTIBLE;
-                        current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                        schedule();
-                } else {
-                        if ((card->secondhalf) || (!card->doubleS0)) {
-                                save_flags(flags);
-                                cli();
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+			schedule();
+		} else {
+			if ((card->secondhalf) || (!card->doubleS0)) {
 #ifdef BOOT_DEBUG
-                                printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
-                                       card->secondhalf);
+				printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
+				       card->secondhalf);
 #endif
-                                init_timer(&card->st_timer);
-                                card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
-                                card->st_timer.function = icn_polldchan;
-                                card->st_timer.data = (unsigned long)card;
-                                add_timer(&card->st_timer);
-                                card->flags |= ICN_FLAGS_RUNNING;
-                                if (card->doubleS0) {
-                                        init_timer(&card->other->st_timer);
-                                        card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
-                                        card->other->st_timer.function = icn_polldchan;
-                                        card->other->st_timer.data = (unsigned long)card->other;
-                                        add_timer(&card->other->st_timer);
-                                        card->other->flags |= ICN_FLAGS_RUNNING;
-                                }
-                                restore_flags(flags);
-                        }
-                        icn_maprelease_channel(card, 0);
-                        return 0;
-                }
-        }
+				save_flags(flags);
+				cli();
+				init_timer(&card->st_timer);
+				card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+				card->st_timer.function = icn_polldchan;
+				card->st_timer.data = (unsigned long) card;
+				add_timer(&card->st_timer);
+				card->flags |= ICN_FLAGS_RUNNING;
+				if (card->doubleS0) {
+					init_timer(&card->other->st_timer);
+					card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+					card->other->st_timer.function = icn_polldchan;
+					card->other->st_timer.data = (unsigned long) card->other;
+					add_timer(&card->other->st_timer);
+					card->other->flags |= ICN_FLAGS_RUNNING;
+				}
+				restore_flags(flags);
+			}
+			icn_maprelease_channel(card, 0);
+			return 0;
+		}
+	}
 }
 
 /* Read the Status-replies from the Interface */
-static int icn_readstatus(u_char * buf, int len, int user, icn_card * card)
+static int
+icn_readstatus(u_char * buf, int len, int user, icn_card * card)
 {
-        int count;
-        u_char *p;
+	int count;
+	u_char *p;
 
-        for (p = buf, count = 0; count < len; p++, count++) {
-                if (card->msg_buf_read == card->msg_buf_write)
-                        return count;
-                if (user)
-                        put_user(*card->msg_buf_read++, p);
-                else
-                        *p = *card->msg_buf_read++;
-                if (card->msg_buf_read > card->msg_buf_end)
-                        card->msg_buf_read = card->msg_buf;
-        }
-        return count;
+	for (p = buf, count = 0; count < len; p++, count++) {
+		if (card->msg_buf_read == card->msg_buf_write)
+			return count;
+		if (user)
+			put_user(*card->msg_buf_read++, p);
+		else
+			*p = *card->msg_buf_read++;
+		if (card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	return count;
 }
 
 /* Put command-strings into the command-queue of the Interface */
-static int icn_writecmd(const u_char * buf, int len, int user, icn_card * card, int waitflg)
+static int
+icn_writecmd(const u_char * buf, int len, int user, icn_card * card)
 {
-        int mch = card->secondhalf ? 2 : 0;
-        int avail;
-        int pp;
-        int i;
-        int count;
-        int ocount;
-        unsigned long flags;
-        u_char *p;
-        isdn_ctrl cmd;
-        u_char msg[0x100];
-
-        while (1) {
-                if (icn_trymaplock_channel(card, mch)) {
-                        avail = cmd_free;
-                        count = MIN(avail, len);
-                        if (user)
-                        {
-                                if (copy_from_user(msg, buf, count) != 0)
-                                {
-                                	icn_release_channel();
-                                	return -EFAULT;
-                                }
-                        }
-                        else
-                                memcpy(msg, buf, count);
-                        save_flags(flags);
-                        cli();
-                        ocount = 1;
-                        *card->msg_buf_write++ = '>';
-                        if (card->msg_buf_write > card->msg_buf_end)
-                                card->msg_buf_write = card->msg_buf;
-                        for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp++) {
-                                writeb((*p == '\n') ? 0xff : *p,
-                                       &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]);
-                                *card->msg_buf_write++ = *p;
-                                if ((*p == '\n') && (i > 1)) {
-                                        *card->msg_buf_write++ = '>';
-                                        if (card->msg_buf_write > card->msg_buf_end)
-                                                card->msg_buf_write = card->msg_buf;
-                                        ocount++;
-                                }
-                                /* No checks for buffer overflow of raw-status-device */
-                                if (card->msg_buf_write > card->msg_buf_end)
-                                        card->msg_buf_write = card->msg_buf;
-                                ocount++;
-                        }
-                        restore_flags(flags);
-                        cmd.command = ISDN_STAT_STAVAIL;
-                        cmd.driver = card->myid;
-                        cmd.arg = ocount;
-                        card->interface.statcallb(&cmd);
-                        writeb((readb(&cmd_i) + count) & 0xff, &cmd_i);
-                        icn_release_channel();
-                        waitflg = 0;
-                } else
-                        count = 0;
-                if (!waitflg)
-                        break;
-                current->timeout = jiffies + 10;
-                schedule();
-        }
-        return count;
+	int mch = card->secondhalf ? 2 : 0;
+	int avail;
+	int pp;
+	int i;
+	int count;
+	int xcount;
+	int ocount;
+	int loop;
+	unsigned long flags;
+	int lastmap_channel;
+	struct icn_card *lastmap_card;
+	u_char *p;
+	isdn_ctrl cmd;
+	u_char msg[0x100];
+
+	ocount = 1;
+	xcount = loop = 0;
+	while (len) {
+		save_flags(flags);
+		cli();
+		lastmap_card = dev.mcard;
+		lastmap_channel = dev.channel;
+		icn_map_channel(card, mch);
+
+		avail = cmd_free;
+		count = MIN(avail, len);
+		if (user)
+			copy_from_user(msg, buf, count);
+		else
+			memcpy(msg, buf, count);
+		icn_putmsg(card, '>');
+		for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp
+		     ++) {
+			writeb((*p == '\n') ? 0xff : *p,
+			   &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]);
+			len--;
+			xcount++;
+			icn_putmsg(card, *p);
+			if ((*p == '\n') && (i > 1)) {
+				icn_putmsg(card, '>');
+				ocount++;
+			}
+			ocount++;
+		}
+		writeb((readb(&cmd_i) + count) & 0xff, &cmd_i);
+		if (lastmap_card)
+			icn_map_channel(lastmap_card, lastmap_channel);
+		restore_flags(flags);
+		if (len) {
+			udelay(1000);
+			if (loop++ > 20)
+				break;
+		} else
+			break;
+	}
+	if (len && (!user))
+		printk(KERN_WARNING "icn: writemsg incomplete!\n");
+	cmd.command = ISDN_STAT_STAVAIL;
+	cmd.driver = card->myid;
+	cmd.arg = ocount;
+	card->interface.statcallb(&cmd);
+	return xcount;
 }
 
 /*
  * Delete card's pending timers, send STOP to linklevel
  */
-static void icn_stopcard(icn_card * card)
+static void
+icn_stopcard(icn_card * card)
 {
-        unsigned long flags;
-        isdn_ctrl cmd;
+	unsigned long flags;
+	isdn_ctrl cmd;
 
-        save_flags(flags);
-        cli();
-        if (card->flags & ICN_FLAGS_RUNNING) {
-                card->flags &= ~ICN_FLAGS_RUNNING;
-                del_timer(&card->st_timer);
-                del_timer(&card->rb_timer);
-                cmd.command = ISDN_STAT_STOP;
-                cmd.driver  = card->myid;
-                card->interface.statcallb(&cmd);
-                if (card->doubleS0)
-                        icn_stopcard(card->other);
-        }
-        restore_flags(flags);
-}
-
-static void icn_stopallcards(void)
-{
-        icn_card *p = cards;
-
-        while (p) {
-                icn_stopcard(p);
-                p = p->next;
-        }
-}
-
-static int icn_command(isdn_ctrl * c, icn_card * card)
-{
-        ulong a;
-        ulong flags;
-        int i;
-        char cbuf[60];
-        isdn_ctrl cmd;
-        icn_cdef cdef;
-
-        switch (c->command) {
-        case ISDN_CMD_IOCTL:
-                memcpy(&a, c->num, sizeof(ulong));
-                switch (c->arg) {
-                        case ICN_IOCTL_SETMMIO:
-                                if ((unsigned long) dev.shmem != (a & 0x0ffc000)) {
-                                        if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
-                                                printk(KERN_WARNING
-                                                       "icn: memory at 0x%08lx in use.\n",
-                                                       (ulong) (a & 0x0ffc000));
-                                                return -EINVAL;
-                                        }
-                                        icn_stopallcards();
-                                        save_flags(flags);
-                                        cli();
-                                        if (dev.mvalid)
-                                                release_shmem((ulong) dev.shmem, 0x4000);
-                                        dev.mvalid = 0;
-                                        dev.shmem = (icn_shmem *) (a & 0x0ffc000);
-                                        restore_flags(flags);
-                                        printk(KERN_INFO
-                                               "icn: (%s) mmio set to 0x%08lx\n",
-                                               CID,
-                                               (unsigned long) dev.shmem);
-                                }
-                                break;
-                        case ICN_IOCTL_GETMMIO:
-                                return (long) dev.shmem;
-                        case ICN_IOCTL_SETPORT:
-                                if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
-                                    || a == 0x340 || a == 0x350 || a == 0x360 ||
-                                    a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
-                                    || a == 0x348 || a == 0x358 || a == 0x368) {
-                                        if (card->port != (unsigned short) a) {
-                                                if (check_region((unsigned short) a, ICN_PORTLEN)) {
-                                                        printk(KERN_WARNING
-                                                               "icn: (%s) ports 0x%03x-0x%03x in use.\n",
-                                                               CID, (int) a, (int) a + ICN_PORTLEN);
-                                                        return -EINVAL;
-                                                }
-                                                icn_stopcard(card);
-                                                save_flags(flags);
-                                                cli();
-                                                if (card->rvalid)
-                                                        release_region(card->port, ICN_PORTLEN);
-                                                card->port = (unsigned short) a;
-                                                card->rvalid = 0;
-                                                if (card->doubleS0) {
-                                                        card->other->port = (unsigned short) a;
-                                                        card->other->rvalid = 0;
-                                                }
-                                                restore_flags(flags);
-                                                printk(KERN_INFO
-                                                       "icn: (%s) port set to 0x%03x\n",
-                                                       CID, card->port);
-                                        }
-                                } else
-                                        return -EINVAL;
-                                break;
-                        case ICN_IOCTL_GETPORT:
-                                return (int) card->port;
-                        case ICN_IOCTL_GETDOUBLE:
-                                return (int) card->doubleS0;
-                        case ICN_IOCTL_DEBUGVAR:
-				if ((i = verify_area(VERIFY_WRITE,
-                                                    (void *) a,
-                                                    sizeof(ulong) * 2)))
-                                        return i;
-                                if (copy_to_user((char *)a,
-                                            (char *)&card, sizeof(ulong)))
-                                            	return -EFAULT;
-				a += sizeof(ulong);
-				{
-                                        ulong l = (ulong)&dev;
-                                        if (copy_to_user((char *)a,
-                                                    (char *)&l, sizeof(ulong)))
-                                                    return -EFAULT;
-                                }
-                                return 0;
-                        case ICN_IOCTL_LOADBOOT:
-                                icn_stopcard(card);
-                                return (icn_loadboot((u_char *) a, card));
-                        case ICN_IOCTL_LOADPROTO:
-                                icn_stopcard(card);
-                                if ((i = (icn_loadproto((u_char *) a, card))))
-                                        return i;
-                                if (card->doubleS0)
-                                        i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), card->other);
-                                return i;
-                                break;
-                        case ICN_IOCTL_ADDCARD:
-                                if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(icn_cdef))))
-                                        return i;
-                                if (copy_from_user((char *)&cdef, (char *)a, sizeof(cdef)))
-                                	return -EFAULT;
-                                return (icn_addcard(cdef.port, cdef.id1, cdef.id2));
-                                break;
-                        case ICN_IOCTL_LEASEDCFG:
-                                if (a) {
-                                        if (!card->leased) {
-                                                card->leased = 1;
-                                                while (card->ptype == ISDN_PTYPE_UNKNOWN) {
-                                                        current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                                                        schedule();
-                                                }
-                                                current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
-                                                schedule();
-                                                sprintf(cbuf, "00;FV2ON\n01;EAZ1\n");
-                                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                                                printk(KERN_INFO
-                                                       "icn: (%s) Leased-line mode enabled\n",
-                                                       CID);
-                                                cmd.command = ISDN_STAT_RUN;
-                                                cmd.driver = card->myid;
-                                                cmd.arg = 0;
-                                                card->interface.statcallb(&cmd);
-                                        }
-                                } else {
-                                        if (card->leased) {
-                                                card->leased = 0;
-                                                sprintf(cbuf, "00;FV2OFF\n");
-                                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                                                printk(KERN_INFO
-                                                       "icn: (%s) Leased-line mode disabled\n",
-                                                       CID);
-                                                cmd.command = ISDN_STAT_RUN;
-                                                cmd.driver = card->myid;
-                                                cmd.arg = 0;
-                                                card->interface.statcallb(&cmd);
-                                        }
-                                }
-                                return 0;
-                        default:
-                                return -EINVAL;
-                }
-                break;
-                case ISDN_CMD_DIAL:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (card->leased)
-                                break;
-                        if ((c->arg & 255) < ICN_BCH) {
-                                char *p;
-                                char *p2;
-                                char dial[50];
-                                char sis[50];
-                                char dcode[4];
-                                int si1, si2;
-
-                                a = c->arg;
-                                strcpy(sis, c->num);
-                                p = strrchr(sis, ',');
-                                *p++ = '\0';
-                                si2 = simple_strtoul(p,NULL,10);
-                                p = strrchr(sis, ',') + 1;
-                                si1 = simple_strtoul(p,NULL,10);
-                                p = c->num;
-                                if (*p == 's' || *p == 'S') {
-                                        /* Dial for SPV */
-                                        p++;
-                                        strcpy(dcode, "SCA");
-                                } else
-                                        /* Normal Dial */
-                                        strcpy(dcode, "CAL");
-                                strcpy(dial, p);
-                                p = strchr(dial, ',');
-                                *p++ = '\0';
-                                p2 = strchr(p, ',');
-                                *p2 = '\0';
-                                sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1), dcode, dial, si1,
-                                        si2, p);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                        }
-                        break;
-                case ISDN_CMD_ACCEPTD:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->fw_rev >= 300) {
-                                        switch (card->l2_proto[a-1]) {
-                                                case ISDN_PROTO_L2_X75I:
-                                                        sprintf(cbuf, "%02d;BX75\n", (int) a);
-                                                        break;
-                                                case ISDN_PROTO_L2_HDLC:
-                                                        sprintf(cbuf, "%02d;BTRA\n", (int) a);
-                                                        break;
-                                        }
-                                        i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                                }
-                                sprintf(cbuf, "%02d;DCON_R\n", (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                        }
-                        break;
-                case ISDN_CMD_ACCEPTB:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->fw_rev >= 300)
-                                        switch (card->l2_proto[a-1]) {
-                                                case ISDN_PROTO_L2_X75I:
-                                                        sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
-                                                        break;
-                                                case ISDN_PROTO_L2_HDLC:
-                                                        sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
-                                                        break;
-                                        }
-                                else
-                                        sprintf(cbuf, "%02d;BCON_R\n", (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                        }
-                        break;
-                case ISDN_CMD_HANGUP:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                        }
-                        break;
-                case ISDN_CMD_SETEAZ:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (card->leased)
-                                break;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->ptype == ISDN_PTYPE_EURO) {
-                                        sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
-                                                c->num[0] ? "N" : "ALL", c->num);
-                                } else
-                                        sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
-                                                c->num[0] ? c->num : "0123456789");
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                        }
-                        break;
-                case ISDN_CMD_CLREAZ:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if (card->leased)
-                                break;
-                        if (c->arg < ICN_BCH) {
-                                a = c->arg + 1;
-                                if (card->ptype == ISDN_PTYPE_EURO)
-                                        sprintf(cbuf, "%02d;MSNC\n", (int) a);
-                                else
-                                        sprintf(cbuf, "%02d;EAZC\n", (int) a);
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                        }
-                        break;
-                case ISDN_CMD_SETL2:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if ((c->arg & 255) < ICN_BCH) {
-                                a = c->arg;
-                                switch (a >> 8) {
-                                        case ISDN_PROTO_L2_X75I:
-                                                sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
-                                                break;
-                                        case ISDN_PROTO_L2_HDLC:
-                                                sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
-                                                break;
-                                        default:
-                                                return -EINVAL;
-                                }
-                                i = icn_writecmd(cbuf, strlen(cbuf), 0, card, 1);
-                                card->l2_proto[a & 255] = (a >> 8);
-                        }
-                        break;
-                case ISDN_CMD_GETL2:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if ((c->arg & 255) < ICN_BCH)
-                                return card->l2_proto[c->arg & 255];
-                        else
-                                return -ENODEV;
-                case ISDN_CMD_SETL3:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        return 0;
-                case ISDN_CMD_GETL3:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        if ((c->arg & 255) < ICN_BCH)
-                                return ISDN_PROTO_L3_TRANS;
-                        else
-                                return -ENODEV;
-                case ISDN_CMD_GETEAZ:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        break;
-                case ISDN_CMD_SETSIL:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        break;
-                case ISDN_CMD_GETSIL:
-                        if (!card->flags & ICN_FLAGS_RUNNING)
-                                return -ENODEV;
-                        break;
-                case ISDN_CMD_LOCK:
-                        MOD_INC_USE_COUNT;
-                        break;
-                case ISDN_CMD_UNLOCK:
-                        MOD_DEC_USE_COUNT;
-                        break;
-                default:
-                        return -EINVAL;
-        }
-        return 0;
+	save_flags(flags);
+	cli();
+	if (card->flags & ICN_FLAGS_RUNNING) {
+		card->flags &= ~ICN_FLAGS_RUNNING;
+		del_timer(&card->st_timer);
+		del_timer(&card->rb_timer);
+		cmd.command = ISDN_STAT_STOP;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		if (card->doubleS0)
+			icn_stopcard(card->other);
+	}
+	restore_flags(flags);
+}
+
+static void
+icn_stopallcards(void)
+{
+	icn_card *p = cards;
+
+	while (p) {
+		icn_stopcard(p);
+		p = p->next;
+	}
 }
 
 /*
- * Find card with given driverId
+ * Unmap all cards, because some of them may be mapped accidetly during
+ * autoprobing of some network drivers (SMC-driver?)
  */
-static inline icn_card *
-  icn_findcard(int driverid)
+static void
+icn_disable_cards(void)
 {
-        icn_card *p = cards;
+	icn_card *card = cards;
+	unsigned long flags;
 
-        while (p) {
-                if (p->myid == driverid)
-                        return p;
-                p = p->next;
-        }
-        return (icn_card *)0;
+	save_flags(flags);
+	cli();
+	while (card) {
+		if (check_region(card->port, ICN_PORTLEN)) {
+			printk(KERN_WARNING
+			       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+			       CID,
+			       card->port,
+			       card->port + ICN_PORTLEN);
+			cli();
+		} else {
+			OUTB_P(0, ICN_RUN);	/* Reset Controller     */
+			OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+		}
+		card = card->next;
+	}
+	restore_flags(flags);
+}
+
+static int
+icn_command(isdn_ctrl * c, icn_card * card)
+{
+	ulong a;
+	ulong flags;
+	int i;
+	char cbuf[60];
+	isdn_ctrl cmd;
+	icn_cdef cdef;
+
+	switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			switch (c->arg) {
+				case ICN_IOCTL_SETMMIO:
+					if ((unsigned long) dev.shmem != (a & 0x0ffc000)) {
+						if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
+							printk(KERN_WARNING
+							       "icn: memory at 0x%08lx in use.\n",
+							       (ulong) (a & 0x0ffc000));
+							return -EINVAL;
+						}
+						icn_stopallcards();
+						save_flags(flags);
+						cli();
+						if (dev.mvalid)
+							release_shmem((ulong) dev.shmem, 0x4000);
+						dev.mvalid = 0;
+						dev.shmem = (icn_shmem *) (a & 0x0ffc000);
+						restore_flags(flags);
+						printk(KERN_INFO
+						       "icn: (%s) mmio set to 0x%08lx\n",
+						       CID,
+						       (unsigned long) dev.shmem);
+					}
+					break;
+				case ICN_IOCTL_GETMMIO:
+					return (long) dev.shmem;
+				case ICN_IOCTL_SETPORT:
+					if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
+					    || a == 0x340 || a == 0x350 || a == 0x360 ||
+					    a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
+					    || a == 0x348 || a == 0x358 || a == 0x368) {
+						if (card->port != (unsigned short) a) {
+							if (check_region((unsigned short) a, ICN_PORTLEN)) {
+								printk(KERN_WARNING
+								       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+								       CID, (int) a, (int) a + ICN_PORTLEN);
+								return -EINVAL;
+							}
+							icn_stopcard(card);
+							save_flags(flags);
+							cli();
+							if (card->rvalid)
+								release_region(card->port, ICN_PORTLEN);
+							card->port = (unsigned short) a;
+							card->rvalid = 0;
+							if (card->doubleS0) {
+								card->other->port = (unsigned short) a;
+								card->other->rvalid = 0;
+							}
+							restore_flags(flags);
+							printk(KERN_INFO
+							       "icn: (%s) port set to 0x%03x\n",
+							CID, card->port);
+						}
+					} else
+						return -EINVAL;
+					break;
+				case ICN_IOCTL_GETPORT:
+					return (int) card->port;
+				case ICN_IOCTL_GETDOUBLE:
+					return (int) card->doubleS0;
+				case ICN_IOCTL_DEBUGVAR:
+					if ((i = verify_area(VERIFY_WRITE,
+							     (void *) a,
+						     sizeof(ulong) * 2)))
+						return i;
+					copy_to_user((char *) a,
+					  (char *) &card, sizeof(ulong));
+					a += sizeof(ulong);
+					{
+						ulong l = (ulong) & dev;
+						copy_to_user((char *) a,
+							     (char *) &l, sizeof(ulong));
+					}
+					return 0;
+				case ICN_IOCTL_LOADBOOT:
+					if (dev.firstload) {
+						icn_disable_cards();
+						dev.firstload = 0;
+					}
+					icn_stopcard(card);
+					return (icn_loadboot((u_char *) a, card));
+				case ICN_IOCTL_LOADPROTO:
+					icn_stopcard(card);
+					if ((i = (icn_loadproto((u_char *) a, card))))
+						return i;
+					if (card->doubleS0)
+						i = icn_loadproto((u_char *) (a + ICN_CODE_STAGE2), card->other);
+					return i;
+					break;
+				case ICN_IOCTL_ADDCARD:
+					if (!dev.firstload)
+						return -EBUSY;
+					if ((i = verify_area(VERIFY_READ, (void *) a, sizeof(icn_cdef))))
+						return i;
+					copy_from_user((char *) &cdef, (char *) a, sizeof(cdef));
+					return (icn_addcard(cdef.port, cdef.id1, cdef.id2));
+					break;
+				case ICN_IOCTL_LEASEDCFG:
+					if (a) {
+						if (!card->leased) {
+							card->leased = 1;
+							while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+								current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+								schedule();
+							}
+							current->timeout = jiffies + ICN_BOOT_TIMEOUT1;
+							schedule();
+							sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n");
+							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "icn: (%s) Leased-line mode enabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					} else {
+						if (card->leased) {
+							card->leased = 0;
+							sprintf(cbuf, "00;FV2OFF\n");
+							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "icn: (%s) Leased-line mode disabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					}
+					return 0;
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if ((c->arg & 255) < ICN_BCH) {
+				char *p;
+				char dial[50];
+				char dcode[4];
+
+				a = c->arg;
+				p = c->parm.setup.phone;
+				if (*p == 's' || *p == 'S') {
+					/* Dial for SPV */
+					p++;
+					strcpy(dcode, "SCA");
+				} else
+					/* Normal Dial */
+					strcpy(dcode, "CAL");
+				strcpy(dial, p);
+				sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1),
+					dcode, dial, c->parm.setup.si1,
+				c->parm.setup.si2, c->parm.setup.eazmsn);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->fw_rev >= 300) {
+					switch (card->l2_proto[a - 1]) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BX75\n", (int) a);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BTRA\n", (int) a);
+							break;
+					}
+					i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->fw_rev >= 300)
+					switch (card->l2_proto[a - 1]) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+							break;
+				} else
+					sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_HANGUP:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_SETEAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->ptype == ISDN_PTYPE_EURO) {
+					sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+						c->parm.num[0] ? "N" : "ALL", c->parm.num);
+				} else
+					sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+						c->parm.num[0] ? c->parm.num : "0123456789");
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_CLREAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->ptype == ISDN_PTYPE_EURO)
+					sprintf(cbuf, "%02d;MSNC\n", (int) a);
+				else
+					sprintf(cbuf, "%02d;EAZC\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_SETL2:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg & 255) < ICN_BCH) {
+				a = c->arg;
+				switch (a >> 8) {
+					case ISDN_PROTO_L2_X75I:
+						sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+						break;
+					case ISDN_PROTO_L2_HDLC:
+						sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+						break;
+					default:
+						return -EINVAL;
+				}
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+				card->l2_proto[a & 255] = (a >> 8);
+			}
+			break;
+		case ISDN_CMD_GETL2:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg & 255) < ICN_BCH)
+				return card->l2_proto[c->arg & 255];
+			else
+				return -ENODEV;
+		case ISDN_CMD_SETL3:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			return 0;
+		case ISDN_CMD_GETL3:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg & 255) < ICN_BCH)
+				return ISDN_PROTO_L3_TRANS;
+			else
+				return -ENODEV;
+		case ISDN_CMD_GETEAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			break;
+		case ISDN_CMD_SETSIL:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			break;
+		case ISDN_CMD_GETSIL:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			break;
+		case ISDN_CMD_LOCK:
+			MOD_INC_USE_COUNT;
+			break;
+		case ISDN_CMD_UNLOCK:
+			MOD_DEC_USE_COUNT;
+			break;
+		default:
+			return -EINVAL;
+	}
+	return 0;
 }
 
 /*
- * Wrapper functions for interface to linklevel
+ * Find card with given driverId
  */
-static int if_command(isdn_ctrl * c)
-{
-        icn_card *card = icn_findcard(c->driver);
-
-        if (card)
-                return (icn_command(c, card));
-        printk(KERN_ERR
-               "icn: if_command %d called with invalid driverId %d!\n",
-		c->command, c->driver);
-        return -ENODEV;
-}
-
-static int if_writecmd(const u_char * buf, int len, int user, int id, int channel)
-{
-        icn_card *card = icn_findcard(id);
-
-        if (card) {
-                if (!card->flags & ICN_FLAGS_RUNNING)
-                        return -ENODEV;
-                return (icn_writecmd(buf, len, user, card, 0));
-        }
-        printk(KERN_ERR
-               "icn: if_writecmd called with invalid driverId!\n");
-        return -ENODEV;
-}
-
-static int if_readstatus(u_char * buf, int len, int user, int id, int channel)
+static inline icn_card *
+icn_findcard(int driverid)
 {
-        icn_card *card = icn_findcard(id);
+	icn_card *p = cards;
 
-        if (card) {
-                if (!card->flags & ICN_FLAGS_RUNNING)
-                        return -ENODEV;
-                return (icn_readstatus(buf, len, user, card));
-        }
-        printk(KERN_ERR
-               "icn: if_readstatus called with invalid driverId!\n");
-        return -ENODEV;
+	while (p) {
+		if (p->myid == driverid)
+			return p;
+		p = p->next;
+	}
+	return (icn_card *) 0;
 }
 
-static int if_sendbuf(int id, int channel, struct sk_buff *skb)
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
 {
-        icn_card *card = icn_findcard(id);
+	icn_card *card = icn_findcard(c->driver);
 
-        if (card) {
-                if (!card->flags & ICN_FLAGS_RUNNING)
-                        return -ENODEV;
-                return (icn_sendbuf(channel, skb, card));
-        }
-        printk(KERN_ERR
-               "icn: if_sendbuf called with invalid driverId!\n");
-        return -ENODEV;
+	if (card)
+		return (icn_command(c, card));
+	printk(KERN_ERR
+	       "icn: if_command %d called with invalid driverId %d!\n",
+	       c->command, c->driver);
+	return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_writecmd(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_writecmd called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_readstatus(buf, len, user, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_readstatus called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, struct sk_buff *skb)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_sendbuf(channel, skb, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_sendbuf called with invalid driverId!\n");
+	return -ENODEV;
 }
 
 /*
  * Allocate a new card-struct, initialize it
  * link it into cards-list and register it at linklevel.
  */
-static icn_card *icn_initcard(int port, char *id) {
-        icn_card *card;
-        int i;
-
-        if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) {
-                printk(KERN_WARNING
-                       "icn: (%s) Could not allocate card-struct.\n", id);
-                return (icn_card *)0;
-        }
-        memset((char *) card, 0, sizeof(icn_card));
-        card->port = port;
-        card->interface.channels = ICN_BCH;
-        card->interface.maxbufsize = 4000;
-        card->interface.command = if_command;
+static icn_card *
+icn_initcard(int port, char *id)
+{
+	icn_card *card;
+	int i;
+
+	if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) {
+		printk(KERN_WARNING
+		       "icn: (%s) Could not allocate card-struct.\n", id);
+		return (icn_card *) 0;
+	}
+	memset((char *) card, 0, sizeof(icn_card));
+	card->port = port;
+	card->interface.channels = ICN_BCH;
+	card->interface.maxbufsize = 4000;
+	card->interface.command = if_command;
 	card->interface.writebuf_skb = if_sendbuf;
-        card->interface.writecmd = if_writecmd;
-        card->interface.readstat = if_readstatus;
-        card->interface.features = ISDN_FEATURE_L2_X75I |
-                ISDN_FEATURE_L2_HDLC |
-                ISDN_FEATURE_L3_TRANS |
-                ISDN_FEATURE_P_UNKNOWN;
-        card->ptype = ISDN_PTYPE_UNKNOWN;
-        strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
-        card->msg_buf_write = card->msg_buf;
-        card->msg_buf_read = card->msg_buf;
-        card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
-        for (i=0;i<ICN_BCH;i++) {
-                card->l2_proto[i] = ISDN_PROTO_L2_X75I;
-                skb_queue_head_init(&card->spqueue[i]);
-        }
-        card->next = cards;
-        cards = card;
-        if (!register_isdn(&card->interface)) {
-                cards = cards->next;
-                printk(KERN_WARNING
-                       "icn: Unable to register %s\n", id);
-                kfree(card);
-                return (icn_card*)0;
-        }
-        card->myid = card->interface.channels;
-        sprintf(card->regname, "icn-isdn (%s)", card->interface.id);
-        return card;
-}
-
-static int icn_addcard(int port, char *id1, char *id2)
-{
-        ulong flags;
-        icn_card *card;
-        icn_card *card2;
-
-        save_flags(flags);
-        cli();
-        if (!(card = icn_initcard(port,id1))) {
-                restore_flags(flags);
-                return -EIO;
-        }
-        if (!strlen(id2)) {
-                restore_flags(flags);
-                printk(KERN_INFO
-                       "icn: (%s) ICN-2B, port 0x%x added\n",
-                       card->interface.id, port);
-                return 0;
-        }
-        if (!(card2 = icn_initcard(port,id2))) {
-                restore_flags(flags);
-                printk(KERN_INFO
-                       "icn: (%s) half ICN-4B, port 0x%x added\n",
-                       card2->interface.id, port);
-                return 0;
-        }
-        card->doubleS0 = 1;
-        card->secondhalf = 0;
-        card->other = card2;
-        card2->doubleS0 = 1;
-        card2->secondhalf = 1;
-        card2->other = card;
-        restore_flags(flags);
-        printk(KERN_INFO
-               "icn: (%s and %s) ICN-4B, port 0x%x added\n",
-               card->interface.id, card2->interface.id, port);
-        return 0;
+	card->interface.writecmd = if_writecmd;
+	card->interface.readstat = if_readstatus;
+	card->interface.features = ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L3_TRANS |
+	    ISDN_FEATURE_P_UNKNOWN;
+	card->ptype = ISDN_PTYPE_UNKNOWN;
+	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+	card->msg_buf_write = card->msg_buf;
+	card->msg_buf_read = card->msg_buf;
+	card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+	for (i = 0; i < ICN_BCH; i++) {
+		card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+		skb_queue_head_init(&card->spqueue[i]);
+	}
+	card->next = cards;
+	cards = card;
+	if (!register_isdn(&card->interface)) {
+		cards = cards->next;
+		printk(KERN_WARNING
+		       "icn: Unable to register %s\n", id);
+		kfree(card);
+		return (icn_card *) 0;
+	}
+	card->myid = card->interface.channels;
+	sprintf(card->regname, "icn-isdn (%s)", card->interface.id);
+	return card;
+}
+
+static int
+icn_addcard(int port, char *id1, char *id2)
+{
+	ulong flags;
+	icn_card *card;
+	icn_card *card2;
+
+	save_flags(flags);
+	cli();
+	if (!(card = icn_initcard(port, id1))) {
+		restore_flags(flags);
+		return -EIO;
+	}
+	if (!strlen(id2)) {
+		restore_flags(flags);
+		printk(KERN_INFO
+		       "icn: (%s) ICN-2B, port 0x%x added\n",
+		       card->interface.id, port);
+		return 0;
+	}
+	if (!(card2 = icn_initcard(port, id2))) {
+		restore_flags(flags);
+		printk(KERN_INFO
+		       "icn: (%s) half ICN-4B, port 0x%x added\n",
+		       card2->interface.id, port);
+		return 0;
+	}
+	card->doubleS0 = 1;
+	card->secondhalf = 0;
+	card->other = card2;
+	card2->doubleS0 = 1;
+	card2->secondhalf = 1;
+	card2->other = card;
+	restore_flags(flags);
+	printk(KERN_INFO
+	       "icn: (%s and %s) ICN-4B, port 0x%x added\n",
+	       card->interface.id, card2->interface.id, port);
+	return 0;
 }
 
 #ifdef MODULE
-EXPORT_NO_SYMBOLS;
 #define icn_init init_module
 #else
-void icn_setup(char *str, int *ints)
+void
+icn_setup(char *str, int *ints)
 {
-        char *p;
-        static char sid[20];
-        static char sid2[20];
-
-        if (ints[0])
-                portbase = ints[1];
-        if (ints[0]>1)
-                membase = ints[2];
-        if (strlen(str)) {
-                strcpy(sid,str);
-                icn_id = sid;
-                if ((p = strchr(sid,','))) {
-                        *p++ = 0;
-                        strcpy(sid2,p);
-                        icn_id2 = sid2;
-                }
-        }
+	char *p;
+	static char sid[20];
+	static char sid2[20];
+
+	if (ints[0])
+		portbase = ints[1];
+	if (ints[0] > 1)
+		membase = ints[2];
+	if (strlen(str)) {
+		strcpy(sid, str);
+		icn_id = sid;
+		if ((p = strchr(sid, ','))) {
+			*p++ = 0;
+			strcpy(sid2, p);
+			icn_id2 = sid2;
+		}
+	}
 }
 #endif
 
-int icn_init(void)
-{
-        char *p;
-        char rev[10];
-
-        memset(&dev, 0, sizeof(icn_dev));
-        dev.shmem = (icn_shmem *) ((unsigned long)membase & 0x0ffc000);
-        dev.channel = -1;
-        dev.mcard   = NULL;
-
-        if ((p = strchr(revision, ':'))) {
-                strcpy(rev, p + 1);
-                p = strchr(rev, '$');
-                *p = 0;
-        } else
-                strcpy(rev, " ??? ");
-        printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
-               (ulong) dev.shmem);
-        return (icn_addcard(portbase,icn_id,icn_id2));
+int
+icn_init(void)
+{
+	char *p;
+	char rev[10];
+
+	memset(&dev, 0, sizeof(icn_dev));
+	dev.shmem = (icn_shmem *) ((unsigned long) membase & 0x0ffc000);
+	dev.channel = -1;
+	dev.mcard = NULL;
+	dev.firstload = 1;
+
+	/* No symbols to export, hide all symbols */
+#if (LINUX_VERSION_CODE < 0x020111)
+	register_symtab(NULL);
+#else
+	EXPORT_NO_SYMBOLS;
+#endif
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+	printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
+	       (ulong) dev.shmem);
+	return (icn_addcard(portbase, icn_id, icn_id2));
 }
 
 #ifdef MODULE
-void cleanup_module(void)
+void
+cleanup_module(void)
 {
-        isdn_ctrl cmd;
-        icn_card *card = cards;
-        icn_card *last;
-        int i;
-
-        icn_stopallcards();
-        while (card) {
-                cmd.command = ISDN_STAT_UNLOAD;
-                cmd.driver = card->myid;
-                card->interface.statcallb(&cmd);
-                if (card->rvalid) {
-                        OUTB_P(0, ICN_RUN);    /* Reset Controller     */
-                        OUTB_P(0, ICN_MAPRAM); /* Disable RAM          */
-                        if (card->secondhalf || (!card->doubleS0)) {
-                                release_region(card->port, ICN_PORTLEN);
-                                card->rvalid = 0;
-                        }
-                        for (i = 0; i < ICN_BCH; i++)
-                                icn_free_queue(&card->spqueue[i]);
-                }
-                card = card->next;
-        }
-        card = cards;
-        while (card) {
-                last = card;
-                card = card->next;
-                kfree(last);
-        }
-        if (dev.mvalid)
-                release_shmem((ulong) dev.shmem, 0x4000);
-        printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
+	isdn_ctrl cmd;
+	icn_card *card = cards;
+	icn_card *last;
+	int i;
+
+	icn_stopallcards();
+	while (card) {
+		cmd.command = ISDN_STAT_UNLOAD;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		if (card->rvalid) {
+			OUTB_P(0, ICN_RUN);	/* Reset Controller     */
+			OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+			if (card->secondhalf || (!card->doubleS0)) {
+				release_region(card->port, ICN_PORTLEN);
+				card->rvalid = 0;
+			}
+			for (i = 0; i < ICN_BCH; i++)
+				icn_free_queue(&card->spqueue[i]);
+		}
+		card = card->next;
+	}
+	card = cards;
+	while (card) {
+		last = card;
+		card = card->next;
+		kfree(last);
+	}
+	if (dev.mvalid)
+		release_shmem((ulong) dev.shmem, 0x4000);
+	printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
 }
 #endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/icn/icn.h linux/drivers/isdn/icn/icn.h
--- v2.1.26/linux/drivers/isdn/icn/icn.h	Mon Nov 18 01:31:31 1996
+++ linux/drivers/isdn/icn/icn.h	Tue Feb 25 17:12:49 1997
@@ -1,5 +1,5 @@
-/* $Id: icn.h,v 1.21 1996/08/29 20:35:57 fritz Exp $
- *
+/* $Id: icn.h,v 1.26 1997/02/14 12:23:16 fritz Exp $
+
  * ISDN lowlevel-module for the ICN active ISDN-Card.
  *
  * Copyright 1994 by Fritz Elfert (fritz@wuemaus.franken.de)
@@ -16,9 +16,26 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: icn.h,v $
+ * Revision 1.26  1997/02/14 12:23:16  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.25  1997/02/10 10:10:31  fritz
+ * Changes for Kernel 2.1.X compatibility.
+ * Enhanced initialization, can recover from
+ * misconfiguration by other autoprobing drivers.
+ *
+ * Revision 1.24  1997/01/29 22:34:46  fritz
+ * Cleanup, Corrected D64S setup of 2nd channel.
+ *
+ * Revision 1.23  1996/12/17 18:47:55  tsbogend
+ * changed timeouts from absolute numbers to HZ based values
+ *
+ * Revision 1.22  1996/11/13 02:37:33  fritz
+ * Added delay include.
+ *
  * Revision 1.21  1996/08/29 20:35:57  fritz
  * Speed up B-Channel polling interval.
  *
@@ -110,15 +127,14 @@
 
 /* Struct for adding new cards */
 typedef struct icn_cdef {
-        int port;
-        char id1[10];
-        char id2[10];
+	int port;
+	char id1[10];
+	char id2[10];
 } icn_cdef;
 
 #if defined(__KERNEL__) || defined(__DEBUGVAR__)
 
 #ifdef __KERNEL__
-
 /* Kernel includes */
 
 #include <linux/module.h>
@@ -128,7 +144,6 @@
 #include <linux/major.h>
 #include <asm/segment.h>
 #include <asm/io.h>
-#include <asm/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/malloc.h>
@@ -137,10 +152,10 @@
 #include <linux/ioport.h>
 #include <linux/timer.h>
 #include <linux/wait.h>
+#include <linux/delay.h>
 #include <linux/isdnif.h>
 
-
-#endif /* __KERNEL__ */
+#endif                          /* __KERNEL__ */
 
 /* some useful macros for debugging */
 #ifdef ICN_DEBUG_PORT
@@ -154,138 +169,154 @@
 #define ICN_PORTLEN (0x04)
 #define ICN_MEMADDR 0x0d0000
 
-#define ICN_FLAGS_B1ACTIVE 1     /* B-Channel-1 is open                     */
-#define ICN_FLAGS_B2ACTIVE 2     /* B-Channel-2 is open                     */
-#define ICN_FLAGS_RUNNING  4     /* Cards driver activated                  */
-#define ICN_FLAGS_RBTIMER  8     /* cyclic scheduling of B-Channel-poll     */
-
-#define ICN_BOOT_TIMEOUT1  100   /* Delay for Boot-download (jiffies)       */
-#define ICN_CHANLOCK_DELAY  10   /* Delay for Channel-mapping (jiffies)     */
-
-#define ICN_TIMER_BCREAD 1       /* B-Channel poll-cycle                    */
-#define ICN_TIMER_DCREAD 50      /* D-Channel poll-cycle                    */
-
-#define ICN_CODE_STAGE1 4096     /* Size of bootcode                        */
-#define ICN_CODE_STAGE2 65536    /* Size of protocol-code                   */
-
-#define ICN_MAX_SQUEUE 8000      /* Max. outstanding send-data (2* hw-buf.) */
-#define ICN_FRAGSIZE (250)       /* Max. size of send-fragments             */
-#define ICN_BCH 2                /* Number of supported channels per card   */
+#define ICN_FLAGS_B1ACTIVE 1    /* B-Channel-1 is open                     */
+#define ICN_FLAGS_B2ACTIVE 2    /* B-Channel-2 is open                     */
+#define ICN_FLAGS_RUNNING  4    /* Cards driver activated                  */
+#define ICN_FLAGS_RBTIMER  8    /* cyclic scheduling of B-Channel-poll     */
+
+#define ICN_BOOT_TIMEOUT1  (HZ) /* Delay for Boot-download (jiffies)       */
+#define ICN_CHANLOCK_DELAY (HZ/10)	/* Delay for Channel-mapping (jiffies)     */
+
+#define ICN_TIMER_BCREAD (HZ/100)	/* B-Channel poll-cycle                    */
+#define ICN_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle                    */
+
+#define ICN_CODE_STAGE1 4096    /* Size of bootcode                        */
+#define ICN_CODE_STAGE2 65536   /* Size of protocol-code                   */
+
+#define ICN_MAX_SQUEUE 8000     /* Max. outstanding send-data (2* hw-buf.) */
+#define ICN_FRAGSIZE (250)      /* Max. size of send-fragments             */
+#define ICN_BCH 2               /* Number of supported channels per card   */
 
 /* type-definitions for accessing the mmap-io-areas */
 
-#define SHM_DCTL_OFFSET (0)      /* Offset to data-controlstructures in shm */
-#define SHM_CCTL_OFFSET (0x1d2)  /* Offset to comm-controlstructures in shm */
-#define SHM_CBUF_OFFSET (0x200)  /* Offset to comm-buffers in shm           */
-#define SHM_DBUF_OFFSET (0x2000) /* Offset to data-buffers in shm           */
+#define SHM_DCTL_OFFSET (0)     /* Offset to data-controlstructures in shm */
+#define SHM_CCTL_OFFSET (0x1d2) /* Offset to comm-controlstructures in shm */
+#define SHM_CBUF_OFFSET (0x200) /* Offset to comm-buffers in shm           */
+#define SHM_DBUF_OFFSET (0x2000)	/* Offset to data-buffers in shm           */
 
 /*
  * Layout of card's data buffers
  */
 typedef struct {
-        unsigned char length;             /* Bytecount of fragment (max 250)    */
-        unsigned char endflag;            /* 0=last frag., 0xff=frag. continued */
-        unsigned char data[ICN_FRAGSIZE]; /* The data                           */
-        /* Fill to 256 bytes */
-        char unused[0x100 - ICN_FRAGSIZE - 2];
+	unsigned char length;   /* Bytecount of fragment (max 250)    */
+	unsigned char endflag;  /* 0=last frag., 0xff=frag. continued */
+	unsigned char data[ICN_FRAGSIZE];	/* The data                           */
+	/* Fill to 256 bytes */
+	char unused[0x100 - ICN_FRAGSIZE - 2];
 } frag_buf;
 
 /*
  * Layout of card's shared memory
  */
 typedef union {
-        struct {
-                unsigned char scns;            /* Index to free SendFrag.             */
-                unsigned char scnr;            /* Index to active SendFrag   READONLY */
-                unsigned char ecns;            /* Index to free RcvFrag.     READONLY */
-                unsigned char ecnr;            /* Index to valid RcvFrag              */
-                char unused[6];
-                unsigned short fuell1;         /* Internal Buf Bytecount              */
-        } data_control;
-        struct {
-                char unused[SHM_CCTL_OFFSET];
-                unsigned char iopc_i;          /* Read-Ptr Status-Queue      READONLY */
-                unsigned char iopc_o;          /* Write-Ptr Status-Queue              */
-                unsigned char pcio_i;          /* Write-Ptr Command-Queue             */
-                unsigned char pcio_o;          /* Read-Ptr Command Queue     READONLY */
-        } comm_control;
-        struct {
-                char unused[SHM_CBUF_OFFSET];
-                unsigned char pcio_buf[0x100]; /* Ring-Buffer Command-Queue           */
-                unsigned char iopc_buf[0x100]; /* Ring-Buffer Status-Queue            */
-        } comm_buffers;
-        struct {
-                char unused[SHM_DBUF_OFFSET];
-                frag_buf receive_buf[0x10];
-                frag_buf send_buf[0x10];
-        } data_buffers;
+	struct {
+		unsigned char scns;	/* Index to free SendFrag.             */
+		unsigned char scnr;	/* Index to active SendFrag   READONLY */
+		unsigned char ecns;	/* Index to free RcvFrag.     READONLY */
+		unsigned char ecnr;	/* Index to valid RcvFrag              */
+		char unused[6];
+		unsigned short fuell1;	/* Internal Buf Bytecount              */
+	} data_control;
+	struct {
+		char unused[SHM_CCTL_OFFSET];
+		unsigned char iopc_i;	/* Read-Ptr Status-Queue      READONLY */
+		unsigned char iopc_o;	/* Write-Ptr Status-Queue              */
+		unsigned char pcio_i;	/* Write-Ptr Command-Queue             */
+		unsigned char pcio_o;	/* Read-Ptr Command Queue     READONLY */
+	} comm_control;
+	struct {
+		char unused[SHM_CBUF_OFFSET];
+		unsigned char pcio_buf[0x100];	/* Ring-Buffer Command-Queue           */
+		unsigned char iopc_buf[0x100];	/* Ring-Buffer Status-Queue            */
+	} comm_buffers;
+	struct {
+		char unused[SHM_DBUF_OFFSET];
+		frag_buf receive_buf[0x10];
+		frag_buf send_buf[0x10];
+	} data_buffers;
 } icn_shmem;
 
 /*
  * Per card driver data
  */
 typedef struct icn_card {
-        struct icn_card *next;        /* Pointer to next device struct    */
-        struct icn_card *other;       /* Pointer to other card for ICN4B  */
-        unsigned short port;          /* Base-port-address                */
-        int myid;                     /* Driver-Nr. assigned by linklevel */
-        int rvalid;                   /* IO-portregion has been requested */
-        int leased;                   /* Flag: This Adapter is connected  */
-                                      /*       to a leased line           */
-        unsigned short flags;         /* Statusflags                      */
-        int doubleS0;                 /* Flag: ICN4B                      */
-        int secondhalf;               /* Flag: Second half of a doubleS0  */
-        int fw_rev;                   /* Firmware revision loaded         */
-        int ptype;                    /* Protocol type (1TR6 or Euro)     */
-        struct timer_list st_timer;   /* Timer for Status-Polls           */
-        struct timer_list rb_timer;   /* Timer for B-Channel-Polls        */
-        u_char rcvbuf[ICN_BCH][4096]; /* B-Channel-Receive-Buffers        */
-        int rcvidx[ICN_BCH];          /* Index for above buffers          */
-        int l2_proto[ICN_BCH];        /* Current layer-2-protocol         */
-        isdn_if interface;            /* Interface to upper layer         */
-        int iptr;                     /* Index to imsg-buffer             */
-        char imsg[60];                /* Internal buf for status-parsing  */
-        char msg_buf[2048];           /* Buffer for status-messages       */
-        char *msg_buf_write;          /* Writepointer for statusbuffer    */
-        char *msg_buf_read;           /* Readpointer for statusbuffer     */
-        char *msg_buf_end;            /* Pointer to end of statusbuffer   */
-        int sndcount[ICN_BCH];        /* Byte-counters for B-Ch.-send     */
-        struct sk_buff_head
-                spqueue[ICN_BCH];     /* Sendqueue                        */
-        char regname[35];             /* Name used for request_region     */
-        u_char xmit_lock[ICN_BCH];    /* Semaphore for pollbchan_send()   */
+	struct icn_card *next;  /* Pointer to next device struct    */
+	struct icn_card *other; /* Pointer to other card for ICN4B  */
+	unsigned short port;    /* Base-port-address                */
+	int myid;               /* Driver-Nr. assigned by linklevel */
+	int rvalid;             /* IO-portregion has been requested */
+	int leased;             /* Flag: This Adapter is connected  */
+	/*       to a leased line           */
+	unsigned short flags;   /* Statusflags                      */
+	int doubleS0;           /* Flag: ICN4B                      */
+	int secondhalf;         /* Flag: Second half of a doubleS0  */
+	int fw_rev;             /* Firmware revision loaded         */
+	int ptype;              /* Protocol type (1TR6 or Euro)     */
+	struct timer_list st_timer;	/* Timer for Status-Polls           */
+	struct timer_list rb_timer;	/* Timer for B-Channel-Polls        */
+	u_char rcvbuf[ICN_BCH][4096];	/* B-Channel-Receive-Buffers        */
+	int rcvidx[ICN_BCH];    /* Index for above buffers          */
+	int l2_proto[ICN_BCH];  /* Current layer-2-protocol         */
+	isdn_if interface;      /* Interface to upper layer         */
+	int iptr;               /* Index to imsg-buffer             */
+	char imsg[60];          /* Internal buf for status-parsing  */
+	char msg_buf[2048];     /* Buffer for status-messages       */
+	char *msg_buf_write;    /* Writepointer for statusbuffer    */
+	char *msg_buf_read;     /* Readpointer for statusbuffer     */
+	char *msg_buf_end;      /* Pointer to end of statusbuffer   */
+	int sndcount[ICN_BCH];  /* Byte-counters for B-Ch.-send     */
+	struct sk_buff_head
+	 spqueue[ICN_BCH];      /* Sendqueue                        */
+	char regname[35];       /* Name used for request_region     */
+	u_char xmit_lock[ICN_BCH];	/* Semaphore for pollbchan_send()   */
 } icn_card;
 
 /*
  * Main driver data
  */
 typedef struct icn_dev {
-        icn_shmem *shmem;             /* Pointer to memory-mapped-buffers */
-        int mvalid;                   /* IO-shmem has been requested      */
-        int channel;                  /* Currently mapped channel         */
-        struct icn_card *mcard;       /* Currently mapped card            */
-        int chanlock;                 /* Semaphore for channel-mapping    */
+	icn_shmem *shmem;       /* Pointer to memory-mapped-buffers */
+	int mvalid;             /* IO-shmem has been requested      */
+	int channel;            /* Currently mapped channel         */
+	struct icn_card *mcard; /* Currently mapped card            */
+	int chanlock;           /* Semaphore for channel-mapping    */
+	int firstload;          /* Flag: firmware never loaded      */
 } icn_dev;
 
 typedef icn_dev *icn_devptr;
 
 #ifdef __KERNEL__
 
-static icn_card *cards  = (icn_card *) 0;
-static u_char   chan2bank[] = { 0, 4, 8, 12 }; /* for icn_map_channel() */ 
+static icn_card *cards = (icn_card *) 0;
+static u_char chan2bank[] =
+{0, 4, 8, 12};                  /* for icn_map_channel() */
 
-static icn_dev  dev;
+static icn_dev dev;
 
 /* With modutils >= 1.1.67 Integers can be changed while loading a
  * module. For this reason define the Port-Base an Shmem-Base as
  * integers.
  */
-int portbase  = ICN_BASEADDR;
-int membase   = ICN_MEMADDR;
-char *icn_id  = "\0";
-char *icn_id2 = "\0";
+static int portbase = ICN_BASEADDR;
+static int membase = ICN_MEMADDR;
+static char *icn_id = "\0";
+static char *icn_id2 = "\0";
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_PARM(portbase, "i");
+MODULE_PARM_DESC(portbase, "Port adress of first card");
+MODULE_PARM(membase, "i");
+MODULE_PARM_DESC(membase, "Shared memory adress of all cards");
+MODULE_PARM(icn_id, "s");
+MODULE_PARM_DESC(icn_id, "ID-String of first card");
+MODULE_PARM(icn_id2, "s");
+MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)");
+#endif
+#endif
 
-#endif                                /* __KERNEL__ */
+#endif                          /* __KERNEL__ */
 
 /* Utility-Macros */
 
@@ -297,11 +328,11 @@
 
 /* Return true, if there is a free transmit-buffer */
 #define sbfree (((readb(&dev.shmem->data_control.scns)+1) & 0xf) != \
-                readb(&dev.shmem->data_control.scnr))
+		readb(&dev.shmem->data_control.scnr))
 
 /* Switch to next transmit-buffer */
 #define sbnext (writeb((readb(&dev.shmem->data_control.scns)+1) & 0xf, \
-                       &dev.shmem->data_control.scns))
+		       &dev.shmem->data_control.scns))
 
 /* Shortcuts for transmit-buffer-access */
 #define sbuf_n dev.shmem->data_control.scns
@@ -311,11 +342,11 @@
 
 /* Return true, if there is receive-data is available */
 #define rbavl  (readb(&dev.shmem->data_control.ecnr) != \
-                readb(&dev.shmem->data_control.ecns))
+		readb(&dev.shmem->data_control.ecns))
 
 /* Switch to next receive-buffer */
 #define rbnext (writeb((readb(&dev.shmem->data_control.ecnr)+1) & 0xf, \
-                       &dev.shmem->data_control.ecnr))
+		       &dev.shmem->data_control.ecnr))
 
 /* Shortcuts for receive-buffer-access */
 #define rbuf_n dev.shmem->data_control.ecnr
@@ -329,8 +360,8 @@
 
 /* Return free space in command-buffer */
 #define cmd_free ((readb(&cmd_i)>=readb(&cmd_o))? \
-                  0x100-readb(&cmd_i)+readb(&cmd_o): \
-                  readb(&cmd_o)-readb(&cmd_i))
+		  0x100-readb(&cmd_i)+readb(&cmd_o): \
+		  readb(&cmd_o)-readb(&cmd_i))
 
 /* Shortcuts for message-buffer-access */
 #define msg_o (dev.shmem->comm_control.iopc_o)
@@ -338,8 +369,8 @@
 
 /* Return length of Message, if avail. */
 #define msg_avail ((readb(&msg_o)>readb(&msg_i))? \
-                   0x100-readb(&msg_o)+readb(&msg_i): \
-                   readb(&msg_i)-readb(&msg_o))
+		   0x100-readb(&msg_o)+readb(&msg_i): \
+		   readb(&msg_i)-readb(&msg_o))
 
 #define CID (card->interface.id)
 
@@ -354,5 +385,5 @@
 #define release_shmem release_region
 #define request_shmem request_region
 
-#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
-#endif /* icn_h */
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* icn_h */
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- v2.1.26/linux/drivers/isdn/isdn_audio.c	Fri Jun  7 06:02:40 1996
+++ linux/drivers/isdn/isdn_audio.c	Tue Feb 25 17:12:50 1997
@@ -1,10 +1,10 @@
-/* $Id: isdn_audio.c,v 1.6 1996/06/06 14:43:31 fritz Exp $
- *
+/* $Id: isdn_audio.c,v 1.7 1997/02/03 22:44:11 fritz Exp $
+
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -17,9 +17,12 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.c,v $
+ * Revision 1.7  1997/02/03 22:44:11  fritz
+ * Reformatted according CodingStyle
+ *
  * Revision 1.6  1996/06/06 14:43:31  fritz
  * Changed to support DTMF decoding on audio playback also.
  *
@@ -47,154 +50,158 @@
 #include "isdn_audio.h"
 #include "isdn_common.h"
 
-char *isdn_audio_revision        = "$Revision: 1.6 $";
+char *isdn_audio_revision = "$Revision: 1.7 $";
 
 /*
  * Misc. lookup-tables.
  */
 
 /* ulaw -> signed 16-bit */
-static short isdn_audio_ulaw_to_s16[] = {
-        0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
-        0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
-        0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
-        0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
-        0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
-        0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
-        0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
-        0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
-        0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
-        0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
-        0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
-        0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
-        0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
-        0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
-        0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
-        0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
-        0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
-        0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
-        0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
-        0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
-        0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
-        0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
-        0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
-        0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
-        0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
-        0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
-        0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
-        0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
-        0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
-        0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
-        0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
-        0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+static short isdn_audio_ulaw_to_s16[] =
+{
+	0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
+	0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
+	0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
+	0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
+	0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
+	0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
+	0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
+	0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
+	0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
+	0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
+	0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
+	0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
+	0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
+	0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
+	0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
+	0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
+	0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
+	0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
+	0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
+	0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
+	0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
+	0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
+	0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
+	0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
+	0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
+	0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
+	0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
+	0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
+	0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
+	0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
+	0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
+	0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
 };
 
 /* alaw -> signed 16-bit */
-static short isdn_audio_alaw_to_s16[] = {
-        0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
-        0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
-        0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
-        0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
-        0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
-        0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
-        0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
-        0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
-        0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
-        0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
-        0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
-        0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
-        0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
-        0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
-        0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
-        0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
-        0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
-        0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
-        0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
-        0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
-        0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
-        0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
-        0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
-        0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
-        0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
-        0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
-        0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
-        0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
-        0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
-        0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
-        0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
-        0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
+static short isdn_audio_alaw_to_s16[] =
+{
+	0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
+	0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
+	0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
+	0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
+	0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
+	0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
+	0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
+	0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
+	0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
+	0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
+	0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
+	0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
+	0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
+	0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
+	0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
+	0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
+	0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
+	0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
+	0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
+	0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
+	0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
+	0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
+	0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
+	0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
+	0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
+	0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
+	0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
+	0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
+	0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
+	0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
+	0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
+	0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
 };
 
 /* alaw -> ulaw */
-static char isdn_audio_alaw_to_ulaw[] = {
-        0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
-        0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
-        0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
-        0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
-        0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
-        0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
-        0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
-        0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
-        0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
-        0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
-        0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
-        0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
-        0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
-        0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
-        0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
-        0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
-        0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
-        0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
-        0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
-        0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
-        0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
-        0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
-        0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
-        0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
-        0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
-        0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
-        0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
-        0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
-        0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
-        0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
-        0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
-        0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+static char isdn_audio_alaw_to_ulaw[] =
+{
+	0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+	0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+	0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+	0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+	0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+	0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+	0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+	0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+	0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+	0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+	0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+	0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+	0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+	0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+	0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+	0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+	0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+	0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+	0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+	0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+	0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+	0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+	0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+	0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+	0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+	0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+	0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+	0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+	0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+	0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+	0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+	0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
 };
 
 /* ulaw -> alaw */
-static char isdn_audio_ulaw_to_alaw[] = {
-        0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
-        0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
-        0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
-        0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
-        0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
-        0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
-        0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
-        0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
-        0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
-        0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
-        0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
-        0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
-        0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
-        0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
-        0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
-        0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
-        0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
-        0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
-        0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
-        0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
-        0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
-        0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
-        0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
-        0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
-        0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
-        0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
-        0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
-        0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
-        0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
-        0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
-        0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
-        0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+static char isdn_audio_ulaw_to_alaw[] =
+{
+	0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+	0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+	0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+	0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+	0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+	0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+	0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+	0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+	0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+	0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+	0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+	0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+	0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+	0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+	0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+	0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+	0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+	0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+	0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+	0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+	0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+	0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+	0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+	0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+	0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+	0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+	0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+	0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+	0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+	0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+	0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+	0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
 };
 
 #define NCOEFF           16     /* number of frequencies to be analyzed       */
@@ -206,68 +213,72 @@
 #define HIGRP             1
 
 typedef struct {
-        int grp;        /* low/high group     */
-        int k;          /* k                  */
-        int k2;         /* k fuer 2. harmonic */
+	int grp;                /* low/high group     */
+	int k;                  /* k                  */
+	int k2;                 /* k fuer 2. harmonic */
 } dtmf_t;
 
 /* For DTMF recognition:
  * 2 * cos(2 * PI * k / N) precalculated for all k
  */
-static int cos2pik[NCOEFF] = {
-        55812,  29528, 53603,  24032, 51193,  14443, 48590,   6517,
-        38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
+static int cos2pik[NCOEFF] =
+{
+	55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517,
+	38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
 };
 
-static dtmf_t dtmf_tones[8] = {
-        { LOGRP,  0,  1 }, /*  697 Hz */
-        { LOGRP,  2,  3 }, /*  770 Hz */
-        { LOGRP,  4,  5 }, /*  852 Hz */
-        { LOGRP,  6,  7 }, /*  941 Hz */
-        { HIGRP,  8,  9 }, /* 1209 Hz */
-        { HIGRP, 10, 11 }, /* 1336 Hz */
-        { HIGRP, 12, 13 }, /* 1477 Hz */
-        { HIGRP, 14, 15 }  /* 1633 Hz */
+static dtmf_t dtmf_tones[8] =
+{
+	{LOGRP, 0, 1},          /*  697 Hz */
+	{LOGRP, 2, 3},          /*  770 Hz */
+	{LOGRP, 4, 5},          /*  852 Hz */
+	{LOGRP, 6, 7},          /*  941 Hz */
+	{HIGRP, 8, 9},          /* 1209 Hz */
+	{HIGRP, 10, 11},        /* 1336 Hz */
+	{HIGRP, 12, 13},        /* 1477 Hz */
+	{HIGRP, 14, 15}         /* 1633 Hz */
 };
 
-static char dtmf_matrix[4][4] = {
-        {'1', '2', '3', 'A'},
-        {'4', '5', '6', 'B'},
-        {'7', '8', '9', 'C'},
-        {'*', '0', '#', 'D'}
+static char dtmf_matrix[4][4] =
+{
+	{'1', '2', '3', 'A'},
+	{'4', '5', '6', 'B'},
+	{'7', '8', '9', 'C'},
+	{'*', '0', '#', 'D'}
 };
 
 #if ((CPU == 386) || (CPU == 486) || (CPU == 586))
 static inline void
 isdn_audio_tlookup(const void *table, void *buff, unsigned long n)
 {
-        __asm__("cld\n"
-                "1:\tlodsb\n\t"
-                "xlatb\n\t"
-                "stosb\n\t"
-                "loop 1b\n\t"
-                ::"b" ((long)table), "c" (n), "D" ((long)buff), "S" ((long)buff)
-                :"bx","cx","di","si","ax");
+	__asm__("cld\n"
+		"1:\tlodsb\n\t"
+		"xlatb\n\t"
+		"stosb\n\t"
+		"loop 1b\n\t"
+      : :  "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff)
+      :        "bx", "cx", "di", "si", "ax");
 }
+
 #else
 static inline void
 isdn_audio_tlookup(const char *table, char *buff, unsigned long n)
 {
-        while (n--) 
-                *buff++ = table[*buff];
+	while (n--)
+		*buff++ = table[*buff];
 }
 #endif
 
 void
 isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
 {
-        isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
+	isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
 }
 
 void
 isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len)
 {
-        isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
+	isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
 }
 
 /*
@@ -278,207 +289,218 @@
  */
 
 
-#define ZEROTRAP    /* turn on the trap as per the MIL-STD */
+#define ZEROTRAP                /* turn on the trap as per the MIL-STD */
 #undef ZEROTRAP
-#define BIAS 0x84   /* define the add-in bias for 16 bit samples */
+#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
 #define CLIP 32635
 
 static unsigned char
-isdn_audio_linear2ulaw(int sample) {
-        static int exp_lut[256] = {
-                0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
-                4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
-                5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
-                5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-                7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
-        };
-        int sign, exponent, mantissa;
-        unsigned char ulawbyte;
-
-        /* Get the sample into sign-magnitude. */
-        sign = (sample >> 8) & 0x80;      /* set aside the sign  */
-        if(sign != 0) sample = -sample;   /* get magnitude       */
-        if(sample > CLIP) sample = CLIP;  /* clip the magnitude  */
-
-        /* Convert from 16 bit linear to ulaw. */
-        sample = sample + BIAS;
-        exponent = exp_lut[( sample >> 7 ) & 0xFF];
-        mantissa = (sample >> (exponent + 3)) & 0x0F;
-        ulawbyte = ~(sign | (exponent << 4) | mantissa);
+isdn_audio_linear2ulaw(int sample)
+{
+	static int exp_lut[256] =
+	{
+		0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+	};
+	int sign,
+	 exponent,
+	 mantissa;
+	unsigned char ulawbyte;
+
+	/* Get the sample into sign-magnitude. */
+	sign = (sample >> 8) & 0x80;	/* set aside the sign  */
+	if (sign != 0)
+		sample = -sample;	/* get magnitude       */
+	if (sample > CLIP)
+		sample = CLIP;  /* clip the magnitude  */
+
+	/* Convert from 16 bit linear to ulaw. */
+	sample = sample + BIAS;
+	exponent = exp_lut[(sample >> 7) & 0xFF];
+	mantissa = (sample >> (exponent + 3)) & 0x0F;
+	ulawbyte = ~(sign | (exponent << 4) | mantissa);
 #ifdef ZEROTRAP
-        /* optional CCITT trap */
-        if (ulawbyte == 0) ulawbyte = 0x02;
+	/* optional CCITT trap */
+	if (ulawbyte == 0)
+		ulawbyte = 0x02;
 #endif
-        return(ulawbyte);
+	return (ulawbyte);
 }
 
 
-static int Mx[3][8] = {
-        { 0x3800, 0x5600, 0,0,0,0,0,0 },
-        { 0x399a, 0x3a9f, 0x4d14, 0x6607, 0,0,0,0 },
-        { 0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607 },
+static int Mx[3][8] =
+{
+	{0x3800, 0x5600, 0, 0, 0, 0, 0, 0},
+	{0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0},
+	{0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607},
 };
 
-static int bitmask[9] = {
-        0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
-}; 
+static int bitmask[9] =
+{
+	0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
+};
 
 static int
-isdn_audio_get_bits (adpcm_state *s, unsigned char **in, int *len)
+isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len)
 {
-        while( s->nleft < s->nbits) {
-                int d = *((*in)++);
-                (*len)--;
-                s->word = (s->word << 8) | d;
-                s->nleft += 8;
-        }
-        s->nleft -= s->nbits;
-        return (s->word >> s->nleft) & bitmask[s->nbits];
+	while (s->nleft < s->nbits) {
+		int d = *((*in)++);
+		(*len)--;
+		s->word = (s->word << 8) | d;
+		s->nleft += 8;
+	}
+	s->nleft -= s->nbits;
+	return (s->word >> s->nleft) & bitmask[s->nbits];
 }
 
 static void
-isdn_audio_put_bits (int data, int nbits, adpcm_state *s,
-                     unsigned char **out, int *len)
+isdn_audio_put_bits(int data, int nbits, adpcm_state * s,
+		    unsigned char **out, int *len)
 {
-        s->word = (s->word << nbits) | (data & bitmask[nbits]);
-        s->nleft += nbits;
-        while(s->nleft >= 8) {
-                int d = (s->word >> (s->nleft-8));
-                *(out[0]++) = d & 255;
-                (*len)++;
-                s->nleft -= 8;
-        }
+	s->word = (s->word << nbits) | (data & bitmask[nbits]);
+	s->nleft += nbits;
+	while (s->nleft >= 8) {
+		int d = (s->word >> (s->nleft - 8));
+		*(out[0]++) = d & 255;
+		(*len)++;
+		s->nleft -= 8;
+	}
 }
 
 adpcm_state *
-isdn_audio_adpcm_init(adpcm_state *s, int nbits)
+isdn_audio_adpcm_init(adpcm_state * s, int nbits)
 {
-        if (!s)
-                s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
-        if (s) {
-                s->a     = 0;
-                s->d     = 5;
-                s->word  = 0;
-                s->nleft = 0;
-                s->nbits = nbits;
-        }
-        return s;
+	if (!s)
+		s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
+	if (s) {
+		s->a = 0;
+		s->d = 5;
+		s->word = 0;
+		s->nleft = 0;
+		s->nbits = nbits;
+	}
+	return s;
 }
 
 dtmf_state *
-isdn_audio_dtmf_init(dtmf_state *s)
+isdn_audio_dtmf_init(dtmf_state * s)
 {
-        if (!s)
-                s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
-        if (s) {
-                s->idx   = 0;
-                s->last  = ' ';
-        }
-        return s;
+	if (!s)
+		s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
+	if (s) {
+		s->idx = 0;
+		s->last = ' ';
+	}
+	return s;
 }
 
 /*
  * Decompression of adpcm data to a/u-law
  *
  */
- 
+
 int
-isdn_audio_adpcm2xlaw (adpcm_state *s, int fmt, unsigned char *in,
-                      unsigned char *out, int len)
+isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
 {
-        int a = s->a;
-        int d = s->d;
-        int nbits = s->nbits;
-        int olen = 0;
-        
-        while (len) {
-                int e = isdn_audio_get_bits(s, &in, &len);
-                int sign;
-
-                if (nbits == 4 && e == 0)
-                        d = 4;
-                sign = (e >> (nbits-1))?-1:1;
-                e &= bitmask[nbits-1];
-                a += sign * ((e << 1) + 1) * d >> 1;
-                if (d & 1)
-                        a++;
-                if (fmt)
-                        *out++ = isdn_audio_ulaw_to_alaw[
-                                 isdn_audio_linear2ulaw(a << 2)];
-                else
-                        *out++ = isdn_audio_linear2ulaw(a << 2);
-                olen++;
-                d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14;
-                if ( d < 5 )
-                        d = 5;     
-        }
-        s->a = a;
-        s->d = d;
-        return olen;
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len) {
+		int e = isdn_audio_get_bits(s, &in, &len);
+		int sign;
+
+		if (nbits == 4 && e == 0)
+			d = 4;
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		if (fmt)
+			*out++ = isdn_audio_ulaw_to_alaw[
+					 isdn_audio_linear2ulaw(a << 2)];
+		else
+			*out++ = isdn_audio_linear2ulaw(a << 2);
+		olen++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
+	s->a = a;
+	s->d = d;
+	return olen;
 }
 
 int
-isdn_audio_2adpcm_flush (adpcm_state *s, unsigned char *out)
+isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out)
 {
 	int olen = 0;
 
-        if (s->nleft)
-                isdn_audio_put_bits(0, 8-s->nleft, s, &out, &olen);
-        return olen;
+	if (s->nleft)
+		isdn_audio_put_bits(0, 8 - s->nleft, s, &out, &olen);
+	return olen;
 }
 
 int
-isdn_audio_xlaw2adpcm (adpcm_state *s, int fmt, unsigned char *in,
-                      unsigned char *out, int len)
+isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
 {
-        int a = s->a;
-        int d = s->d;
-        int nbits = s->nbits;
-        int olen = 0;
-
-        while (len--) {
-                int e = 0, nmax = 1 << (nbits - 1);
-                int sign, delta;
-              
-                if (fmt)
-                        delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
-                else
-                        delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
-                if (delta < 0) {
-                        e = nmax;
-                        delta = -delta;
-                }
-                while( --nmax && delta > d ) {
-                        delta -= d;
-                        e++;
-                }
-                if (nbits == 4 && ((e & 0x0f) == 0))
-                        e = 8;
-                isdn_audio_put_bits(e, nbits, s, &out, &olen);
-                sign = (e >> (nbits-1))?-1:1 ;
-                e &= bitmask[nbits-1];
-                
-                a += sign * ((e << 1) + 1) * d >> 1;
-                if (d & 1)
-                        a++;
-                d = (d * Mx[nbits-2][ e ] + 0x2000) >> 14;
-                if (d < 5)
-                        d=5;
-        }
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len--) {
+		int e = 0,
+		 nmax = 1 << (nbits - 1);
+		int sign,
+		 delta;
+
+		if (fmt)
+			delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
+		else
+			delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
+		if (delta < 0) {
+			e = nmax;
+			delta = -delta;
+		}
+		while (--nmax && delta > d) {
+			delta -= d;
+			e++;
+		}
+		if (nbits == 4 && ((e & 0x0f) == 0))
+			e = 8;
+		isdn_audio_put_bits(e, nbits, s, &out, &olen);
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
 	s->a = a;
 	s->d = d;
-        return olen;
+	return olen;
 }
 
 /*
@@ -488,99 +510,108 @@
  * Result is stored into an sk_buff and queued up for later
  * evaluation.
  */
-void
-isdn_audio_goertzel(int *sample, modem_info *info) {
-        int sk, sk1, sk2;
-        int k, n;
-        struct sk_buff *skb;
-        int *result;
-
-        skb = dev_alloc_skb(sizeof(int) * NCOEFF);
-        if (!skb) {
-                printk(KERN_WARNING
-                       "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
-                       info->line);
-                return;
-        }
-        result = (int *)skb_put(skb, sizeof(int) * NCOEFF);
-        skb->free = 1;
-        skb->users = 0;
-        for (k = 0; k < NCOEFF; k++) {
-                sk = sk1 = sk2 = 0;
-                for (n = 0; n < DTMF_NPOINTS; n++) {
-                        sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
-                        sk2 = sk1;
-                        sk1 = sk;
-                }
-                result[k] =
-                        ((sk * sk) >> AMP_BITS) - 
-                        ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
-                        ((sk2 * sk2) >> AMP_BITS);
-        }
-        skb_queue_tail(&info->dtmf_queue, skb);
-        isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+static void
+isdn_audio_goertzel(int *sample, modem_info * info)
+{
+	int sk,
+	 sk1,
+	 sk2;
+	int k,
+	 n;
+	struct sk_buff *skb;
+	int *result;
+
+	skb = dev_alloc_skb(sizeof(int) * NCOEFF);
+	if (!skb) {
+		printk(KERN_WARNING
+		  "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
+		       info->line);
+		return;
+	}
+	SET_SKB_FREE(skb);
+	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+		printk(KERN_WARNING
+		"isdn_audio: insufficient DTMF skb_headroom, dropping\n");
+		dev_kfree_skb(skb, FREE_READ);
+		return;
+	}
+	result = (int *) skb_put(skb, sizeof(int) * NCOEFF);
+	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+	for (k = 0; k < NCOEFF; k++) {
+		sk = sk1 = sk2 = 0;
+		for (n = 0; n < DTMF_NPOINTS; n++) {
+			sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
+			sk2 = sk1;
+			sk1 = sk;
+		}
+		result[k] =
+		    ((sk * sk) >> AMP_BITS) -
+		    ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
+		    ((sk2 * sk2) >> AMP_BITS);
+	}
+	skb_queue_tail(&info->dtmf_queue, skb);
+	isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
 }
 
 void
-isdn_audio_eval_dtmf(modem_info *info)
+isdn_audio_eval_dtmf(modem_info * info)
 {
-        struct sk_buff *skb;
-        int *result;
-        dtmf_state *s;
-        int silence;
-        int i;
-        int di;
-        int ch;
-        unsigned long flags;
-        int grp[2];
-        char what;
-        char *p;
-
-        while ((skb = skb_dequeue(&info->dtmf_queue))) {
-                result = (int *)skb->data;
-                s = info->dtmf_state;
-                grp[LOGRP] = grp[HIGRP] = -2;
-                silence = 0;
-                for(i = 0; i < 8; i++) {
-                        if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
-                            (result[dtmf_tones[i].k2] < H2_TRESH)    )
-                                grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2)?i:-1;
-                        else
-                                if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
-                                    (result[dtmf_tones[i].k2] < SILENCE_TRESH)  )
-                                        silence++;
-                }
-                if(silence == 8)
-                        what = ' ';
-                else {
-                        if((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
-                                what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
-                                if(s->last != ' ' && s->last != '.')
-                                        s->last = what;  /* min. 1 non-DTMF between DTMF */
-                        } else
-                                what = '.';
-                }
-                if ((what != s->last) && (what != ' ') && (what != '.')) {
-                        printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
-                        p = skb->data;
-                        *p++ = 0x10;
-                        *p = what;
-                        skb_trim(skb, 2);
-                        save_flags(flags);
-                        cli();
-                        di = info->isdn_driver;
-                        ch = info->isdn_channel;
-                        __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
-                        dev->drv[di]->rcvcount[ch] += 2;
-                        restore_flags(flags);
-                        /* Schedule dequeuing */
-                        if ((dev->modempoll) && (info->rcvsched))
-                                isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-                        wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
-                } else
-                        kfree_skb(skb, FREE_READ);
-                s->last = what;
-        }
+	struct sk_buff *skb;
+	int *result;
+	dtmf_state *s;
+	int silence;
+	int i;
+	int di;
+	int ch;
+	unsigned long flags;
+	int grp[2];
+	char what;
+	char *p;
+
+	while ((skb = skb_dequeue(&info->dtmf_queue))) {
+		result = (int *) skb->data;
+		s = info->dtmf_state;
+		grp[LOGRP] = grp[HIGRP] = -2;
+		silence = 0;
+		for (i = 0; i < 8; i++) {
+			if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
+			    (result[dtmf_tones[i].k2] < H2_TRESH))
+				grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2) ? i : -1;
+			else if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
+			      (result[dtmf_tones[i].k2] < SILENCE_TRESH))
+				silence++;
+		}
+		if (silence == 8)
+			what = ' ';
+		else {
+			if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
+				what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
+				if (s->last != ' ' && s->last != '.')
+					s->last = what;	/* min. 1 non-DTMF between DTMF */
+			} else
+				what = '.';
+		}
+		if ((what != s->last) && (what != ' ') && (what != '.')) {
+			printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
+			p = skb->data;
+			*p++ = 0x10;
+			*p = what;
+			skb_trim(skb, 2);
+			save_flags(flags);
+			cli();
+			di = info->isdn_driver;
+			ch = info->isdn_channel;
+			__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+			dev->drv[di]->rcvcount[ch] += 2;
+			restore_flags(flags);
+			/* Schedule dequeuing */
+			if ((dev->modempoll) && (info->rcvsched))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+			wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+		} else
+			kfree_skb(skb, FREE_READ);
+		s->last = what;
+	}
 }
 
 /*
@@ -593,30 +624,28 @@
  *   fmt  = audio data format (0 = ulaw, 1 = alaw)
  */
 void
-isdn_audio_calc_dtmf(modem_info *info, unsigned char *buf, int len, int fmt)
+isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt)
 {
-        dtmf_state *s = info->dtmf_state;
-        int i;
-        int c;
-
-        while (len) {
-                c = MIN(len, (DTMF_NPOINTS - s->idx));
-                if (c <= 0)
-                        break;
-                for (i = 0; i < c; i++) {
-                        if (fmt)
-                                s->buf[s->idx++] =
-                                        isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
-                        else
-                                s->buf[s->idx++] =
-                                        isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
-                }
-                if (s->idx == DTMF_NPOINTS) {
-                        isdn_audio_goertzel(s->buf, info);
-                        s->idx = 0;
-                }
-                len -= c;
-        }
+	dtmf_state *s = info->dtmf_state;
+	int i;
+	int c;
+
+	while (len) {
+		c = MIN(len, (DTMF_NPOINTS - s->idx));
+		if (c <= 0)
+			break;
+		for (i = 0; i < c; i++) {
+			if (fmt)
+				s->buf[s->idx++] =
+				    isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
+			else
+				s->buf[s->idx++] =
+				    isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
+		}
+		if (s->idx == DTMF_NPOINTS) {
+			isdn_audio_goertzel(s->buf, info);
+			s->idx = 0;
+		}
+		len -= c;
+	}
 }
-
-
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_audio.h linux/drivers/isdn/isdn_audio.h
--- v2.1.26/linux/drivers/isdn/isdn_audio.h	Fri Jun  7 06:02:40 1996
+++ linux/drivers/isdn/isdn_audio.h	Tue Feb 25 17:12:50 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_audio.h,v 1.4 1996/06/06 14:43:32 fritz Exp $
- *
+/* $Id: isdn_audio.h,v 1.5 1997/02/03 22:45:21 fritz Exp $
+
  * Linux ISDN subsystem, audio conversion and compression (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -16,9 +16,12 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_audio.h,v $
+ * Revision 1.5  1997/02/03 22:45:21  fritz
+ * Reformatted according CodingStyle
+ *
  * Revision 1.4  1996/06/06 14:43:32  fritz
  * Changed to support DTMF decoding on audio playback also.
  *
@@ -33,27 +36,27 @@
  *
  */
 
-#define DTMF_NPOINTS 205       /* Number of samples for DTMF recognition */
+#define DTMF_NPOINTS 205        /* Number of samples for DTMF recognition */
 typedef struct adpcm_state {
-        int  a;
-        int  d;
-        int  word;
-        int  nleft;
-        int  nbits;
+	int a;
+	int d;
+	int word;
+	int nleft;
+	int nbits;
 } adpcm_state;
 
 typedef struct dtmf_state {
-        char last;
-        int  idx;
-        int  buf[DTMF_NPOINTS];
+	char last;
+	int idx;
+	int buf[DTMF_NPOINTS];
 } dtmf_state;
 
 extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
 extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
 extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
-extern int  isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
-extern int  isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
-extern int  isdn_audio_2adpcm_flush(adpcm_state *s, unsigned char *out);
+extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out);
 extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
 extern void isdn_audio_eval_dtmf(modem_info *);
 dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_cards.c linux/drivers/isdn/isdn_cards.c
--- v2.1.26/linux/drivers/isdn/isdn_cards.c	Sun Apr 21 01:56:14 1996
+++ linux/drivers/isdn/isdn_cards.c	Tue Feb 25 17:12:50 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_cards.c,v 1.1 1996/04/20 16:04:36 fritz Exp $
- *
+/* $Id: isdn_cards.c,v 1.3 1997/02/03 23:31:14 fritz Exp $
+
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -16,9 +16,15 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.c,v $
+ * Revision 1.3  1997/02/03 23:31:14  fritz
+ * Reformatted according CodingStyle
+ *
+ * Revision 1.2  1996/10/13 19:52:17  keil
+ * HiSax support
+ *
  * Revision 1.1  1996/04/20 16:04:36  fritz
  * Initial revision
  *
@@ -34,20 +40,27 @@
 extern void teles_init(void);
 #endif
 
+#ifdef CONFIG_ISDN_DRV_HISAX
+extern void HiSax_init(void);
+#endif
+
 #ifdef CONFIG_ISDN_DRV_PCBIT
 extern void pcbit_init(void);
 #endif
 
-void isdn_cards_init(void)
+void
+isdn_cards_init(void)
 {
 #if CONFIG_ISDN_DRV_ICN
-        icn_init();
+	icn_init();
 #endif
 #if CONFIG_ISDN_DRV_TELES
-        teles_init();
+	teles_init();
+#endif
+#ifdef CONFIG_ISDN_DRV_HISAX
+	HiSax_init();
 #endif
 #if CONFIG_ISDN_DRV_PCBIT
-        pcbit_init();
+	pcbit_init();
 #endif
 }
-
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_cards.h linux/drivers/isdn/isdn_cards.h
--- v2.1.26/linux/drivers/isdn/isdn_cards.h	Sun Apr 21 01:56:14 1996
+++ linux/drivers/isdn/isdn_cards.h	Tue Feb 25 17:12:50 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_cards.h,v 1.1 1996/04/20 16:04:03 fritz Exp $
- *
+/* $Id: isdn_cards.h,v 1.2 1997/02/03 23:31:55 fritz Exp $
+
  * Linux ISDN subsystem, initialization for non-modularized drivers.
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -16,13 +16,15 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_cards.h,v $
+ * Revision 1.2  1997/02/03 23:31:55  fritz
+ * Reformatted according CodingStyle
+ *
  * Revision 1.1  1996/04/20 16:04:03  fritz
  * Initial revision
  *
  */
 
 extern void isdn_cards_init(void);
-
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.1.26/linux/drivers/isdn/isdn_common.c	Fri Dec 27 02:03:22 1996
+++ linux/drivers/isdn/isdn_common.c	Tue Feb 25 17:12:50 1997
@@ -1,5 +1,5 @@
-/* $Id: isdn_common.c,v 1.23 1996/06/25 18:35:38 fritz Exp $
- *
+/* $Id: isdn_common.c,v 1.34 1997/02/10 20:12:43 fritz Exp $
+
  * Linux ISDN subsystem, common used functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
@@ -21,6 +21,48 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.c,v $
+ * Revision 1.34  1997/02/10 20:12:43  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.33  1997/02/10 10:05:42  fritz
+ * More changes for Kernel 2.1.X
+ * Symbol information moved to isdn_syms.c
+ *
+ * Revision 1.32  1997/02/03 22:55:26  fritz
+ * Reformatted according CodingStyle.
+ * Changed isdn_writebuf_stub static.
+ * Slow down tty-RING counter.
+ * skb->free stuff replaced by macro.
+ * Bugfix in audio-skb locking.
+ * Bugfix in HL-driver locking.
+ *
+ * Revision 1.31  1997/01/17 01:19:18  fritz
+ * Applied chargeint patch.
+ *
+ * Revision 1.30  1997/01/14 01:27:47  fritz
+ * Changed audio receive not to rely on skb->users and skb->lock.
+ * Added ATI2 and related variables.
+ * Started adding full-duplex audio capability.
+ *
+ * Revision 1.29  1997/01/12 23:33:03  fritz
+ * Made isdn_all_eaz foolproof.
+ *
+ * Revision 1.28  1996/11/13 02:33:19  fritz
+ * Fixed a race condition.
+ *
+ * Revision 1.27  1996/10/27 22:02:41  keil
+ * return codes for ISDN_STAT_ICALL
+ *
+ * Revision 1.26  1996/10/23 11:59:40  fritz
+ * More compatibility changes.
+ *
+ * Revision 1.25  1996/10/22 23:13:54  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
+ * Revision 1.24  1996/10/11 14:02:03  fritz
+ * Bugfix: call to isdn_ppp_timer_timeout() never compiled, because of
+ *         typo in #ifdef.
+ *
  * Revision 1.23  1996/06/25 18:35:38  fritz
  * Fixed bogus memory access in isdn_set_allcfg().
  *
@@ -117,13 +159,14 @@
  *
  */
 
-#include <asm/uaccess.h>
 #include <linux/config.h>
+#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/version.h>
-#ifndef __GENKSYMS__      /* Don't want genksyms report unneeded structs */
-#include <linux/isdn.h>
+#if (LINUX_VERSION_CODE >= 0x020117)
+#include <asm/poll.h>
 #endif
+#include <linux/isdn.h>
 #include "isdn_common.h"
 #include "isdn_tty.h"
 #include "isdn_net.h"
@@ -139,8 +182,7 @@
 
 isdn_dev *dev = (isdn_dev *) 0;
 
-static int  has_exported = 0;
-static char *isdn_revision      = "$Revision: 1.23 $";
+static char *isdn_revision = "$Revision: 1.34 $";
 
 extern char *isdn_net_revision;
 extern char *isdn_tty_revision;
@@ -155,18 +197,23 @@
 static char *isdn_audio_revision = ": none $";
 #endif
 
-void isdn_MOD_INC_USE_COUNT(void)
+static int isdn_writebuf_stub(int, int, const u_char *, int, int);
+
+void
+isdn_MOD_INC_USE_COUNT(void)
 {
 	MOD_INC_USE_COUNT;
 }
 
-void isdn_MOD_DEC_USE_COUNT(void)
+void
+isdn_MOD_DEC_USE_COUNT(void)
 {
 	MOD_DEC_USE_COUNT;
 }
 
 #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
-void isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+void
+isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
 {
 	int dumpc;
 
@@ -177,26 +224,29 @@
 }
 #endif
 
-static __inline void isdn_trash_skb(struct sk_buff *skb, int rw)
+static __inline void
+isdn_trash_skb(struct sk_buff *skb, int rw)
 {
-        skb->free = 1;
-        kfree_skb(skb, rw);
+	SET_SKB_FREE(skb);
+	kfree_skb(skb, rw);
 }
 
-static void isdn_free_queue(struct sk_buff_head *queue)
+static void
+isdn_free_queue(struct sk_buff_head *queue)
 {
-        struct sk_buff *skb;
-        unsigned long flags;
+	struct sk_buff *skb;
+	unsigned long flags;
 
-        save_flags(flags);
-        cli();
-        if (skb_queue_len(queue))
-                while ((skb = skb_dequeue(queue)))
-                        isdn_trash_skb(skb, FREE_READ);
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (skb_queue_len(queue))
+		while ((skb = skb_dequeue(queue)))
+			isdn_trash_skb(skb, FREE_READ);
+	restore_flags(flags);
 }
 
-int isdn_dc2minor(int di, int ch)
+int
+isdn_dc2minor(int di, int ch)
 {
 	int i;
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
@@ -207,8 +257,10 @@
 
 static int isdn_timer_cnt1 = 0;
 static int isdn_timer_cnt2 = 0;
+static int isdn_timer_cnt3 = 0;
 
-static void isdn_timer_funct(ulong dummy)
+static void
+isdn_timer_funct(ulong dummy)
 {
 	int tf = dev->tflags;
 
@@ -226,26 +278,29 @@
 			if (tf & ISDN_TIMER_NETDIAL)
 				isdn_net_dial();
 		}
-                if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
-                        isdn_timer_cnt2 = 0;
-                        if (tf & ISDN_TIMER_NETHANGUP)
-                                isdn_net_autohup();
-                        if (tf & ISDN_TIMER_MODEMRING)
-                                isdn_tty_modem_ring();
-#if (defined CONFIG_ISDN_PPP ) && (defined ISDN_CONFIG_MPP)
-                        if (tf & ISDN_TIMER_IPPP)
-                                isdn_ppp_timer_timeout();
+		if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
+			isdn_timer_cnt2 = 0;
+			if (tf & ISDN_TIMER_NETHANGUP)
+				isdn_net_autohup();
+			if (++isdn_timer_cnt3 > ISDN_TIMER_RINGING) {
+				isdn_timer_cnt3 = 0;
+				if (tf & ISDN_TIMER_MODEMRING)
+					isdn_tty_modem_ring();
+			}
+#if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP)
+			if (tf & ISDN_TIMER_IPPP)
+				isdn_ppp_timer_timeout();
 #endif
-                }
+		}
 	}
 	if (tf) {
-                int flags;
+		int flags;
 
 		save_flags(flags);
 		cli();
-                del_timer(&dev->timer);
+		del_timer(&dev->timer);
 #ifndef NEW_ISDN_TIMER_CTRL
-                dev->timer.function = isdn_timer_funct;
+		dev->timer.function = isdn_timer_funct;
 #endif
 		dev->timer.expires = jiffies + ISDN_TIMER_RES;
 		add_timer(&dev->timer);
@@ -253,7 +308,8 @@
 	}
 }
 
-void isdn_timer_ctrl(int tf, int onoff)
+void
+isdn_timer_ctrl(int tf, int onoff)
 {
 	int flags;
 
@@ -270,14 +326,14 @@
 		dev->tflags &= ~tf;
 #ifdef NEW_ISDN_TIMER_CTRL
 	if (dev->tflags) {
-                if (!del_timer(&dev->timer)) /* del_timer is 1, when active */
-                        dev->timer.expires = jiffies + ISDN_TIMER_RES;
+		if (!del_timer(&dev->timer))	/* del_timer is 1, when active */
+			dev->timer.expires = jiffies + ISDN_TIMER_RES;
 		add_timer(&dev->timer);
 	}
 #else
 	if (dev->tflags) {
-                del_timer(&dev->timer);
-                dev->timer.function = isdn_timer_funct;
+		del_timer(&dev->timer);
+		dev->timer.function = isdn_timer_funct;
 		dev->timer.expires = jiffies + ISDN_TIMER_RES;
 		add_timer(&dev->timer);
 	}
@@ -288,258 +344,277 @@
 /*
  * Receive a packet from B-Channel. (Called from low-level-module)
  */
-static void isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
+static void
+isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
 {
 	ulong flags;
 	int i;
-        int midx;
+	int midx;
 #ifdef CONFIG_ISDN_AUDIO
-        int ifmt;
+	int ifmt;
 #endif
 	modem_info *info;
 
-        if ((i = isdn_dc2minor(di,channel))==-1) {
-                isdn_trash_skb(skb, FREE_READ);
-                return;
-        }
+	if ((i = isdn_dc2minor(di, channel)) == -1) {
+		isdn_trash_skb(skb, FREE_READ);
+		return;
+	}
 	/* Update statistics */
-        dev->ibytes[i] += skb->len;
+	dev->ibytes[i] += skb->len;
 	/* First, try to deliver data to network-device */
 	if (isdn_net_rcv_skb(i, skb))
 		return;
 	/* No network-device found, deliver to tty or raw-channel */
-        skb->free = 1;
+	SET_SKB_FREE(skb);
 	if (skb->len) {
-                if ((midx = dev->m_idx[i])<0) {
-                        /* if midx is invalid, drop packet */
-                        isdn_trash_skb(skb, FREE_READ);
-                        return;
-                }
-                info  = &dev->mdm.info[midx];
+		if ((midx = dev->m_idx[i]) < 0) {
+			/* if midx is invalid, drop packet */
+			isdn_trash_skb(skb, FREE_READ);
+			return;
+		}
+		info = &dev->mdm.info[midx];
 #ifdef CONFIG_ISDN_AUDIO
-                ifmt = 1;
+		ifmt = 1;
 
-                if (info->vonline)
-                        isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+		if (info->vonline)
+			isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
 #endif
-                if ((info->online < 2) &&
-                    (!(info->vonline & 1))) {
-                        /* If Modem not listening, drop data */
-                        isdn_trash_skb(skb, FREE_READ);
-                        return;
-                }
-                if (info->emu.mdmreg[13] & 2)
-                        /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
-                        if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
-                                skb_pull(skb,4);
-                /* The users field of an sk_buff is used in a special way
-                 * with tty's incoming data:
-                 *   users is set to the number of DLE codes when in audio mode.
-                 */
-                skb->users = 0;
+		if ((info->online < 2) &&
+		    (!(info->vonline & 1))) {
+			/* If Modem not listening, drop data */
+			isdn_trash_skb(skb, FREE_READ);
+			return;
+		}
+		if (info->emu.mdmreg[13] & 2)
+			/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+			if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
+				skb_pull(skb, 4);
+		if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+			printk(KERN_WARNING
+			       "isdn_audio: insufficient skb_headroom, dropping\n");
+			isdn_trash_skb(skb, FREE_READ);
+			return;
+		}
+		ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+		ISDN_AUDIO_SKB_LOCK(skb) = 0;
 #ifdef CONFIG_ISDN_AUDIO
-                if (info->vonline & 1) {
-                        /* voice conversion/compression */
-                        switch (info->emu.vpar[3]) {
-                                case 2:
-                                case 3:
-                                case 4:
-                                        /* adpcm
-                                         * Since compressed data takes less
-                                         * space, we can overwrite the buffer.
-                                         */
-                                        skb_trim(skb,isdn_audio_xlaw2adpcm(info->adpcmr,
-                                                                           ifmt,
-                                                                           skb->data,
-                                                                           skb->data,
-                                                                           skb->len));
-                                        break;
-                                case 5:
-                                        /* a-law */
-                                        if (!ifmt)
-                                                isdn_audio_ulaw2alaw(skb->data,skb->len);
-                                        break;
-                                case 6:
-                                        /* u-law */
-                                        if (ifmt)
-                                                isdn_audio_alaw2ulaw(skb->data,skb->len);
-                                        break;
-                        }
-                        skb->users = isdn_tty_countDLE(skb->data,skb->len);
-                }
-#endif
-                /* Try to deliver directly via tty-flip-buf if queue is empty */
-                save_flags(flags);
-                cli();
-                if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
-                        if (isdn_tty_try_read(info, skb)) {
-                                restore_flags(flags);
-                                return;
-                        }
-                /* Direct deliver failed or queue wasn't empty.
-                 * Queue up for later dequeueing via timer-irq.
-                 */
-                __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
-                dev->drv[di]->rcvcount[channel] += (skb->len + skb->users);
-                restore_flags(flags);
-                /* Schedule dequeuing */
-                if ((dev->modempoll) && (info->rcvsched))
-                        isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-                wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+		if (info->vonline & 1) {
+			/* voice conversion/compression */
+			switch (info->emu.vpar[3]) {
+				case 2:
+				case 3:
+				case 4:
+					/* adpcm
+					 * Since compressed data takes less
+					 * space, we can overwrite the buffer.
+					 */
+					skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
+								    ifmt,
+							       skb->data,
+							       skb->data,
+							      skb->len));
+					break;
+				case 5:
+					/* a-law */
+					if (!ifmt)
+						isdn_audio_ulaw2alaw(skb->data, skb->len);
+					break;
+				case 6:
+					/* u-law */
+					if (ifmt)
+						isdn_audio_alaw2ulaw(skb->data, skb->len);
+					break;
+			}
+			ISDN_AUDIO_SKB_DLECOUNT(skb) =
+			    isdn_tty_countDLE(skb->data, skb->len);
+		}
+#endif
+		/* Try to deliver directly via tty-flip-buf if queue is empty */
+		save_flags(flags);
+		cli();
+		if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+			if (isdn_tty_try_read(info, skb)) {
+				restore_flags(flags);
+				return;
+			}
+		/* Direct deliver failed or queue wasn't empty.
+		 * Queue up for later dequeueing via timer-irq.
+		 */
+		__skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
+		dev->drv[di]->rcvcount[channel] +=
+		    (skb->len + ISDN_AUDIO_SKB_DLECOUNT(skb));
+		restore_flags(flags);
+		/* Schedule dequeuing */
+		if ((dev->modempoll) && (info->rcvsched))
+			isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+		wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
 	} else
-                isdn_trash_skb(skb, FREE_READ);
+		isdn_trash_skb(skb, FREE_READ);
 }
 
-void isdn_all_eaz(int di, int ch)
+void
+isdn_all_eaz(int di, int ch)
 {
 	isdn_ctrl cmd;
 
+	if (di < 0)
+		return;
 	cmd.driver = di;
 	cmd.arg = ch;
 	cmd.command = ISDN_CMD_SETEAZ;
-	cmd.num[0] = '\0';
+	cmd.parm.num[0] = '\0';
 	(void) dev->drv[di]->interface->command(&cmd);
 }
 
-static int isdn_status_callback(isdn_ctrl * c)
+static int
+isdn_status_callback(isdn_ctrl * c)
 {
 	int di;
 	int mi;
 	ulong flags;
 	int i;
 	int r;
-        modem_info *info;
+	int retval = 0;
+	modem_info *info;
 	isdn_ctrl cmd;
 
 	di = c->driver;
-        i = isdn_dc2minor(di, c->arg);
+	i = isdn_dc2minor(di, c->arg);
 	switch (c->command) {
-                case ISDN_STAT_BSENT:
-			if (i<0)
+		case ISDN_STAT_BSENT:
+			if (i < 0)
 				return -1;
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        if (isdn_net_stat_callback(i, c->command))
-                                return 0;
-                        isdn_tty_bsent(di, c->arg);
-                        wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
-                        break;
-                case ISDN_STAT_STAVAIL:
-                        save_flags(flags);
-                        cli();
-                        dev->drv[di]->stavail += c->arg;
-                        restore_flags(flags);
-                        wake_up_interruptible(&dev->drv[di]->st_waitq);
-                        break;
-                case ISDN_STAT_RUN:
-                        dev->drv[di]->running = 1;
-                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-                                if (dev->drvmap[i] == di)
-                                        isdn_all_eaz(di, dev->chanmap[i]);
-                        break;
-                case ISDN_STAT_STOP:
-                        dev->drv[di]->running = 0;
-                        break;
-                case ISDN_STAT_ICALL:
-			if (i<0)
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c->command))
+				return 0;
+			isdn_tty_bsent(di, c->arg);
+			wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+			break;
+		case ISDN_STAT_STAVAIL:
+			save_flags(flags);
+			cli();
+			dev->drv[di]->stavail += c->arg;
+			restore_flags(flags);
+			wake_up_interruptible(&dev->drv[di]->st_waitq);
+			break;
+		case ISDN_STAT_RUN:
+			dev->drv[di]->running = 1;
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di)
+					isdn_all_eaz(di, dev->chanmap[i]);
+			break;
+		case ISDN_STAT_STOP:
+			dev->drv[di]->running = 0;
+			break;
+		case ISDN_STAT_ICALL:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->num);
+			printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->num);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
-                                cmd.driver = di;
-                                cmd.arg = c->arg;
-                                cmd.command = ISDN_CMD_HANGUP;
-                                dev->drv[di]->interface->command(&cmd);
-                                return 0;
-                        }
-
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_HANGUP;
+				dev->drv[di]->interface->command(&cmd);
+				return 0;
+			}
 			/* Try to find a network-interface which will accept incoming call */
-                        cmd.driver = di;
-                        cmd.arg = c->arg;
-                        cmd.command = ISDN_CMD_LOCK;
-                        dev->drv[di]->interface->command(&cmd);
-			r = isdn_net_find_icall(di, c->arg, i, c->num);
+			cmd.driver = di;
+			cmd.arg = c->arg;
+			cmd.command = ISDN_CMD_LOCK;
+			dev->drv[di]->interface->command(&cmd);
+			r = isdn_net_find_icall(di, c->arg, i, c->parm.setup);
 			switch (r) {
-                                case 0:
-                                        /* No network-device replies. Schedule RING-message to
-                                         * tty and set RI-bit of modem-status.
-                                         */
-                                        if ((mi = isdn_tty_find_icall(di, c->arg, c->num)) >= 0) {
-                                                info = &dev->mdm.info[mi];
-                                                info->msr |= UART_MSR_RI;
-                                                isdn_tty_modem_result(2, info);
-                                                isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
-                                                return 0;
-                                        } else if (dev->drv[di]->reject_bus) {
-                                                cmd.driver = di;
-                                                cmd.arg = c->arg;
-                                                cmd.command = ISDN_CMD_HANGUP;
-                                                dev->drv[di]->interface->command(&cmd);
-                                        }
-                                        break;
-                                case 1:
-                                        /* Schedule connection-setup */
-                                        isdn_net_dial();
-                                        cmd.driver = di;
-                                        cmd.arg = c->arg;
-                                        cmd.command = ISDN_CMD_ACCEPTD;
-                                        dev->drv[di]->interface->command(&cmd);
-                                        return 0;
-                                        break;
-                                case 2:	/* For calling back, first reject incoming call ... */
-                                case 3:	/* Interface found, but down, reject call actively  */
-                                        printk(KERN_INFO "isdn: Rejecting Call\n");
-                                        cmd.driver = di;
-                                        cmd.arg = c->arg;
-                                        cmd.command = ISDN_CMD_HANGUP;
-                                        dev->drv[di]->interface->command(&cmd);
-                                        if (r == 3)
-                                                break;
-                                        /* Fall through */
-                                case 4:
-                                        /* ... then start callback. */
-                                        isdn_net_dial();
-                                        return 0;
+				case 0:
+					/* No network-device replies. Schedule RING-message to
+					 * tty and set RI-bit of modem-status.
+					 */
+					if ((mi = isdn_tty_find_icall(di, c->arg, c->parm.setup)) >= 0) {
+						info = &dev->mdm.info[mi];
+						info->msr |= UART_MSR_RI;
+						isdn_tty_modem_result(2, info);
+						isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+						retval = 1;
+					} else if (dev->drv[di]->reject_bus) {
+						cmd.driver = di;
+						cmd.arg = c->arg;
+						cmd.command = ISDN_CMD_HANGUP;
+						dev->drv[di]->interface->command(&cmd);
+						retval = 2;
+					}
+					break;
+				case 1:
+					/* Schedule connection-setup */
+					isdn_net_dial();
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_ACCEPTD;
+					dev->drv[di]->interface->command(&cmd);
+					retval = 1;
+					break;
+				case 2:	/* For calling back, first reject incoming call ... */
+				case 3:	/* Interface found, but down, reject call actively  */
+					retval = 2;
+					printk(KERN_INFO "isdn: Rejecting Call\n");
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_HANGUP;
+					dev->drv[di]->interface->command(&cmd);
+					if (r == 3)
+						break;
+					/* Fall through */
+				case 4:
+					/* ... then start callback. */
+					isdn_net_dial();
+					break;
+			}
+			if (retval != 1) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_UNLOCK;
+				dev->drv[di]->interface->command(&cmd);
 			}
-                        cmd.driver = di;
-                        cmd.arg = c->arg;
-                        cmd.command = ISDN_CMD_UNLOCK;
-                        dev->drv[di]->interface->command(&cmd);
-                        return 0;
-                        break;
-                case ISDN_STAT_CINF:
-			if (i<0)
+			return retval;
+			break;
+		case ISDN_STAT_CINF:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->num);
+			printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->num);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        if (strcmp(c->num, "0"))
-                                isdn_net_stat_callback(i, c->command);
-                        break;
-                case ISDN_STAT_CAUSE:
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (strcmp(c->parm.num, "0"))
+				isdn_net_stat_callback(i, c->command);
+			break;
+		case ISDN_STAT_CAUSE:
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->num);
+			printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->num);
 #endif
-                        printk(KERN_INFO "isdn: cause: %s\n", c->num);
-                        break;
-                case ISDN_STAT_DCONN:
-			if (i<0)
+			printk(KERN_INFO "isdn: cause: %s\n", c->parm.num);
+			if ((mi = dev->m_idx[i]) >= 0) {
+				/* Signal cause to tty-device */
+				info = &dev->mdm.info[mi];
+				strncpy(info->last_cause, c->parm.num, 5);
+			}
+			break;
+		case ISDN_STAT_DCONN:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+			printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        /* Find any network-device, waiting for D-channel setup */
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			/* Find any network-device, waiting for D-channel setup */
+			if (isdn_net_stat_callback(i, c->command))
+				break;
 
 			if ((mi = dev->m_idx[i]) >= 0) {
 				/* If any tty has just dialed-out, setup B-Channel */
-                                info = &dev->mdm.info[mi];
+				info = &dev->mdm.info[mi];
 				if (info->flags &
 				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
 					if (info->dialing == 1) {
@@ -551,24 +626,24 @@
 						return 0;
 					}
 				}
-                        }
-                        break;
-                case ISDN_STAT_DHUP:
-			if (i<0)
+			}
+			break;
+		case ISDN_STAT_DHUP:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
+			printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        dev->drv[di]->flags &= ~(1 << (c->arg));
-                        isdn_info_update();
-                        /* Signal hangup to network-devices */
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->flags &= ~(1 << (c->arg));
+			isdn_info_update();
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c->command))
+				break;
 			if ((mi = dev->m_idx[i]) >= 0) {
 				/* Signal hangup to tty-device */
-                                info = &dev->mdm.info[mi];
+				info = &dev->mdm.info[mi];
 				if (info->flags &
 				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
 					if (info->dialing == 1) {
@@ -580,55 +655,58 @@
 #ifdef ISDN_DEBUG_MODEM_HUP
 					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
 #endif
-					isdn_tty_modem_hup(info);
+					isdn_tty_modem_hup(info, 0);
 					return 0;
 				}
 			}
-                        break;
-                case ISDN_STAT_BCONN:
-			if (i<0)
+			break;
+		case ISDN_STAT_BCONN:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+			printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
 #endif
-                        /* Signal B-channel-connect to network-devices */
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        dev->drv[di]->flags |= (1 << (c->arg));
-                        isdn_info_update();
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
+			/* Signal B-channel-connect to network-devices */
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->flags |= (1 << (c->arg));
+			isdn_info_update();
+			if (isdn_net_stat_callback(i, c->command))
+				break;
 			if ((mi = dev->m_idx[i]) >= 0) {
 				/* Schedule CONNECT-Message to any tty, waiting for it and
 				 * set DCD-bit of its modem-status.
 				 */
-                                info = &dev->mdm.info[mi];
+				info = &dev->mdm.info[mi];
 				if (info->flags &
 				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
 					info->msr |= UART_MSR_DCD;
-					if (info->dialing)
+					if (info->dialing) {
 						info->dialing = 0;
+						info->last_dir = 1;
+					} else
+						info->last_dir = 0;
 					info->rcvsched = 1;
-                                        if (USG_MODEM(dev->usage[i]))
-                                          isdn_tty_modem_result(5, info);
-                                        if (USG_VOICE(dev->usage[i]))
-                                          isdn_tty_modem_result(11, info);
+					if (USG_MODEM(dev->usage[i]))
+						isdn_tty_modem_result(5, info);
+					if (USG_VOICE(dev->usage[i]))
+						isdn_tty_modem_result(11, info);
 				}
 			}
-                        break;
-                case ISDN_STAT_BHUP:
-			if (i<0)
+			break;
+		case ISDN_STAT_BHUP:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
+			printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        dev->drv[di]->flags &= ~(1 << (c->arg));
-                        isdn_info_update();
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->flags &= ~(1 << (c->arg));
+			isdn_info_update();
 			if ((mi = dev->m_idx[i]) >= 0) {
 				/* Signal hangup to tty-device, schedule NO CARRIER-message */
-                                info = &dev->mdm.info[mi];
+				info = &dev->mdm.info[mi];
 				if (info->flags &
 				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
 					if (info->msr & UART_MSR_DCD)
@@ -637,26 +715,29 @@
 #ifdef ISDN_DEBUG_MODEM_HUP
 					printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
 #endif
-					isdn_tty_modem_hup(info);
+					isdn_tty_modem_hup(info, 0);
 				}
 			}
-                        break;
-                case ISDN_STAT_NODCH:
-			if (i<0)
+			break;
+		case ISDN_STAT_NODCH:
+			if (i < 0)
 				return -1;
 #ifdef ISDN_DEBUG_STATCALLB
-                        printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+			printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
 #endif
-                        if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-                                return 0;
-                        if (isdn_net_stat_callback(i, c->command))
-                                break;
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c->command))
+				break;
 			if ((mi = dev->m_idx[i]) >= 0) {
-                                info = &dev->mdm.info[mi];
+				info = &dev->mdm.info[mi];
 				if (info->flags &
 				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
 					if (info->dialing) {
 						info->dialing = 0;
+						info->last_l2 = -1;
+						info->last_si = 0;
+						sprintf(info->last_cause, "0000");
 						isdn_tty_modem_result(6, info);
 					}
 					info->msr &= ~UART_MSR_DCD;
@@ -666,46 +747,46 @@
 					}
 				}
 			}
-                        break;
-                case ISDN_STAT_ADDCH:
-                        break;
-                case ISDN_STAT_UNLOAD:
-                        save_flags(flags);
-                        cli();
-                        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-                                if (dev->drvmap[i] == di) {
-                                        dev->drvmap[i] = -1;
-                                        dev->chanmap[i] = -1;
-                                }
-                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                                modem_info *info = &dev->mdm.info[i];
-
-                                if (info->isdn_driver == di) {
-                                        info->isdn_driver = -1;
-                                        info->isdn_channel = -1;
-                                        if (info->online) {
-                                                isdn_tty_modem_result(3, info);
-                                                isdn_tty_modem_hup(info);
-                                        }
-                                }
-                        }
-                        dev->drivers--;
-                        dev->channels -= dev->drv[di]->channels;
-                        kfree(dev->drv[di]->rcverr);
-                        kfree(dev->drv[di]->rcvcount);
-                        for (i = 0; i < dev->drv[di]->channels; i++)
-                                isdn_free_queue(&dev->drv[di]->rpqueue[i]);
-                        kfree(dev->drv[di]->rpqueue);
-                        kfree(dev->drv[di]->rcv_waitq);
-                        kfree(dev->drv[di]->snd_waitq);
-                        kfree(dev->drv[di]);
-                        dev->drv[di] = NULL;
-                        dev->drvid[di][0] = '\0';
-                        isdn_info_update();
-                        restore_flags(flags);
-                        return 0;
-                default:
-                        return -1;
+			break;
+		case ISDN_STAT_ADDCH:
+			break;
+		case ISDN_STAT_UNLOAD:
+			save_flags(flags);
+			cli();
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di) {
+					dev->drvmap[i] = -1;
+					dev->chanmap[i] = -1;
+				}
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+				modem_info *info = &dev->mdm.info[i];
+
+				if (info->isdn_driver == di) {
+					info->isdn_driver = -1;
+					info->isdn_channel = -1;
+					if (info->online) {
+						isdn_tty_modem_result(3, info);
+						isdn_tty_modem_hup(info, 1);
+					}
+				}
+			}
+			dev->drivers--;
+			dev->channels -= dev->drv[di]->channels;
+			kfree(dev->drv[di]->rcverr);
+			kfree(dev->drv[di]->rcvcount);
+			for (i = 0; i < dev->drv[di]->channels; i++)
+				isdn_free_queue(&dev->drv[di]->rpqueue[i]);
+			kfree(dev->drv[di]->rpqueue);
+			kfree(dev->drv[di]->rcv_waitq);
+			kfree(dev->drv[di]->snd_waitq);
+			kfree(dev->drv[di]);
+			dev->drv[di] = NULL;
+			dev->drvid[di][0] = '\0';
+			isdn_info_update();
+			restore_flags(flags);
+			return 0;
+		default:
+			return -1;
 	}
 	return 0;
 }
@@ -713,7 +794,8 @@
 /*
  * Get integer from char-pointer, set pointer to end of number
  */
-int isdn_getnum(char **p)
+int
+isdn_getnum(char **p)
 {
 	int v = -1;
 
@@ -727,19 +809,17 @@
 /*
  * isdn_readbchan() tries to get data from the read-queue.
  * It MUST be called with interrupts off.
- *
- * I hope I got the EFAULT error path right -AK.
  */
-int isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user)
+int
+isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, int user)
 {
 	int left;
 	int count;
 	int count_pull;
-        int count_put;
+	int count_put;
 	int dflag;
-        struct sk_buff *skb;
+	struct sk_buff *skb;
 	u_char *cp;
-	int ret = 0;
 
 	if (!dev->drv[di])
 		return 0;
@@ -753,106 +833,98 @@
 	cp = buf;
 	count = 0;
 	while (left) {
-		        ret = 0;
-                if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
-                        break;
-                if (skb->lock)
-                        break;
-                skb->lock = 1;
-                if (skb->users) {
-                        /* users is the count of DLE's in
-                         * this buff when in voice mode.
-                         */
-                        char *p = skb->data;
-                        unsigned long DLEmask = (1 << channel);
-
-                        dflag = 0;
-                        count_pull = count_put = 0;
-                        while ((count_pull < skb->len) && (left-- > 0)) {
-                                if (dev->drv[di]->DLEflag & DLEmask) {
-									    if (user) {
-                                                ret = put_user(DLE,cp);
-												cp++;
-												if (ret) break;
-                                        } else
-                                                *cp++ = DLE;
-                                        dev->drv[di]->DLEflag &= ~DLEmask;
-                                } else {
-									    if (user) {
-                                                ret = put_user(*p,cp);
-												if (ret) break;
-												cp++;
-                                        } else
-                                                *cp++ = *p;
-                                        if (*p == DLE) {
-                                                dev->drv[di]->DLEflag |= DLEmask;
-                                                skb->users--;
-                                        }
-                                        p++;
-                                        count_pull++;
-                                }
-                                count_put++;
-                        }
-                        if (count_pull >= skb->len)
-                                dflag = 1;
-                } else {
-                        /* No DLE's in buff, so simply copy it */
-                        dflag = 1;
-                        if ((count_pull = skb->len) > left) {
-                                count_pull = left;
-                                dflag = 0;
-                        }
-                        count_put = count_pull;
-						ret = 0;
-                        if (user)
-                                ret = copy_to_user(cp, skb->data, count_put);
-                        else
-                                memcpy(cp, skb->data, count_put);
-						count_put -= ret;
-                        cp += count_put;
-                        left -= count_put;
-                }
-                count += count_put;
-                if (fp) {
-                        memset(fp, 0, count_put);
-                        fp += count_put;
-                }
-                if (dflag) {
-                        /* We got all the data in this buff.
-                         * Now we can dequeue it.
-                         */
+		if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+			break;
+		if (ISDN_AUDIO_SKB_LOCK(skb))
+			break;
+		ISDN_AUDIO_SKB_LOCK(skb) = 1;
+		if (ISDN_AUDIO_SKB_DLECOUNT(skb)) {
+			char *p = skb->data;
+			unsigned long DLEmask = (1 << channel);
+
+			dflag = 0;
+			count_pull = count_put = 0;
+			while ((count_pull < skb->len) && (left-- > 0)) {
+				if (dev->drv[di]->DLEflag & DLEmask) {
+					if (user)
+						put_user(DLE, cp++);
+					else
+						*cp++ = DLE;
+					dev->drv[di]->DLEflag &= ~DLEmask;
+				} else {
+					if (user)
+						put_user(*p, cp++);
+					else
+						*cp++ = *p;
+					if (*p == DLE) {
+						dev->drv[di]->DLEflag |= DLEmask;
+						(ISDN_AUDIO_SKB_DLECOUNT(skb))--;
+					}
+					p++;
+					count_pull++;
+				}
+				count_put++;
+			}
+			if (count_pull >= skb->len)
+				dflag = 1;
+		} else {
+			/* No DLE's in buff, so simply copy it */
+			dflag = 1;
+			if ((count_pull = skb->len) > left) {
+				count_pull = left;
+				dflag = 0;
+			}
+			count_put = count_pull;
+			if (user)
+				copy_to_user(cp, skb->data, count_put);
+			else
+				memcpy(cp, skb->data, count_put);
+			cp += count_put;
+			left -= count_put;
+		}
+		count += count_put;
+		if (fp) {
+			memset(fp, 0, count_put);
+			fp += count_put;
+		}
+		if (dflag) {
+			/* We got all the data in this buff.
+			 * Now we can dequeue it.
+			 */
 			if (fp)
 				*(fp - 1) = 0xff;
-                        skb->lock = 0;
-                        skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
-                        isdn_trash_skb(skb, FREE_READ);
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+			isdn_trash_skb(skb, FREE_READ);
 		} else {
-                        /* Not yet emptied this buff, so it
-                         * must stay in the queue, for further calls
-                         * but we pull off the data we got until now.
-                         */
-			skb_pull(skb,count_pull);
-                        skb->lock = 0;
-                }
+			/* Not yet emptied this buff, so it
+			 * must stay in the queue, for further calls
+			 * but we pull off the data we got until now.
+			 */
+			skb_pull(skb, count_pull);
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+		}
 		dev->drv[di]->rcvcount[channel] -= count_put;
 	}
-	return ret ? -EFAULT : count;
+	return count;
 }
 
-static __inline int isdn_minor2drv(int minor)
+static __inline int
+isdn_minor2drv(int minor)
 {
 	return (dev->drvmap[minor]);
 }
 
-static __inline int isdn_minor2chan(int minor)
+static __inline int
+isdn_minor2chan(int minor)
 {
 	return (dev->chanmap[minor]);
 }
 
-#define INF_DV 0x01 /* Data version for /dev/isdninfo */
+#define INF_DV 0x01             /* Data version for /dev/isdninfo */
 
 static char *
- isdn_statstr(void)
+isdn_statstr(void)
 {
 	static char istatbuf[2048];
 	char *p;
@@ -905,7 +977,8 @@
 
 /* Module interface-code */
 
-void isdn_info_update(void)
+void
+isdn_info_update(void)
 {
 	infostruct *p = dev->infochain;
 
@@ -916,7 +989,8 @@
 	wake_up_interruptible(&(dev->info_waitq));
 }
 
-static long isdn_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static RWTYPE
+isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count)
 {
 	uint minor = MINOR(inode->i_rdev);
 	int len = 0;
@@ -927,15 +1001,14 @@
 	if (minor == ISDN_MINOR_STATUS) {
 		char *p;
 		if (!file->private_data) {
-                        if (file->f_flags & O_NONBLOCK)
-                                return -EAGAIN;
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
 			interruptible_sleep_on(&(dev->info_waitq));
-                }
+		}
 		p = isdn_statstr();
 		file->private_data = 0;
 		if ((len = strlen(p)) <= count) {
-			if (copy_to_user(buf, p, len))
-				return -EFAULT;
+			copy_to_user(buf, p, len);
 			file->f_pos += len;
 			return len;
 		}
@@ -950,11 +1023,11 @@
 		if (!dev->drv[drvidx]->running)
 			return -ENODEV;
 		chidx = isdn_minor2chan(minor);
-                save_flags(flags);
-                cli();
+		save_flags(flags);
+		cli();
 		len = isdn_readbchan(drvidx, chidx, buf, 0, count, 1);
 		file->f_pos += len;
-                restore_flags(flags);
+		restore_flags(flags);
 		return len;
 	}
 	if (minor <= ISDN_MINOR_CTRLMAX) {
@@ -962,10 +1035,10 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		if (!dev->drv[drvidx]->stavail) {
-                        if (file->f_flags & O_NONBLOCK)
-                                return -EAGAIN;
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
 			interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
-                }
+		}
 		if (dev->drv[drvidx]->interface->readstat)
 			len = dev->drv[drvidx]->interface->
 			    readstat(buf, MIN(count, dev->drv[drvidx]->stavail),
@@ -974,10 +1047,10 @@
 			len = 0;
 		save_flags(flags);
 		cli();
-                if (len)
-                        dev->drv[drvidx]->stavail -= len;
-                else
-                        dev->drv[drvidx]->stavail = 0;
+		if (len)
+			dev->drv[drvidx]->stavail -= len;
+		else
+			dev->drv[drvidx]->stavail = 0;
 		restore_flags(flags);
 		file->f_pos += len;
 		return len;
@@ -989,13 +1062,14 @@
 	return -ENODEV;
 }
 
-static long long isdn_lseek(struct inode *inode, struct file *file, long long offset, int orig)
+static LSTYPE
+isdn_lseek(struct inode *inode, struct file *file, LSARG offset, int orig)
 {
 	return -ESPIPE;
 }
 
-static long isdn_write(struct inode *inode, struct file *file,
-	const char *buf, unsigned long count)
+static RWTYPE
+isdn_write(struct inode *inode, struct file *file, const char *buf, RWARG count)
 {
 	uint minor = MINOR(inode->i_rdev);
 	int drvidx;
@@ -1039,10 +1113,12 @@
 	return -ENODEV;
 }
 
-static int isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
+#if (LINUX_VERSION_CODE < 0x020117)
+static int
+isdn_select(struct inode *inode, struct file *file, int type, select_table * st)
 {
 	uint minor = MINOR(inode->i_rdev);
-        int drvidx  = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
 
 	if (minor == ISDN_MINOR_STATUS) {
 		if (file->private_data)
@@ -1057,22 +1133,59 @@
 		if (drvidx < 0)
 			return -ENODEV;
 		if (dev->drv[drvidx]->stavail)
-                        return 1;
-                else {
-                        if (st)
-                                select_wait(&(dev->drv[drvidx]->st_waitq), st);
-                        return 0;
-                }
+			return 1;
+		else {
+			if (st)
+				select_wait(&(dev->drv[drvidx]->st_waitq), st);
+			return 0;
+		}
 		return 1;
-	 }
+	}
 #ifdef CONFIG_ISDN_PPP
 	if (minor <= ISDN_MINOR_PPPMAX)
 		return (isdn_ppp_select(minor - ISDN_MINOR_PPP, file, type, st));
 #endif
 	return -ENODEV;
 }
+#else
+static unsigned int
+isdn_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+	unsigned int minor = MINOR(file->f_inode->i_rdev);
+	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+
+	if (minor == ISDN_MINOR_STATUS) {
+		poll_wait(&(dev->info_waitq), wait);
+		/* mask = POLLOUT | POLLWRNORM; */
+		if (file->private_data) {
+			mask |= POLLIN | POLLRDNORM;
+		}
+		return mask;
+	}
+	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
+		poll_wait(&(dev->drv[drvidx]->st_waitq), wait);
+		if (drvidx < 0) {
+			printk(KERN_ERR "isdn_common: isdn_poll 1 -> what the hell\n");
+			return POLLERR;
+		}
+		mask = POLLOUT | POLLWRNORM;
+		if (dev->drv[drvidx]->stavail) {
+			mask |= POLLIN | POLLRDNORM;
+		}
+		return mask;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX)
+		return (isdn_ppp_poll(file, wait));
+#endif
+	printk(KERN_ERR "isdn_common: isdn_poll 2 -> what the hell\n");
+	return POLLERR;
+}
+#endif
 
-static int isdn_set_allcfg(char *src)
+static int
+isdn_set_allcfg(char *src)
 {
 	int ret;
 	int i;
@@ -1085,25 +1198,35 @@
 		return ret;
 	save_flags(flags);
 	cli();
-	ret = get_user(i, src);
-	if (ret)
-		goto out;
-    src += sizeof(int);
+	if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(int)))) {
+		restore_flags(flags);
+		return ret;
+	}
+	copy_from_user((char *) &i, src, sizeof(int));
+	src += sizeof(int);
 	while (i) {
 		char *c;
 		char *c2;
 
-		if(copy_from_user((char *) &cfg, src, sizeof(cfg)))
-			goto fault;
+		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(cfg)))) {
+			restore_flags(flags);
+			return ret;
+		}
+		copy_from_user((char *) &cfg, src, sizeof(cfg));
 		src += sizeof(cfg);
 		if (!isdn_net_new(cfg.name, NULL)) {
 			restore_flags(flags);
 			return -EIO;
 		}
-		if ((ret = isdn_net_setcfg(&cfg)))
-			goto out;
-		if(copy_from_user(buf, src, sizeof(buf)))
-			goto fault;
+		if ((ret = isdn_net_setcfg(&cfg))) {
+			restore_flags(flags);
+			return ret;
+		}
+		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
+			restore_flags(flags);
+			return ret;
+		}
+		copy_from_user(buf, src, sizeof(buf));
 		src += sizeof(buf);
 		c = buf;
 		while (*c) {
@@ -1112,15 +1235,20 @@
 			strcpy(phone.phone, c);
 			strcpy(phone.name, cfg.name);
 			phone.outgoing = 0;
-			if ((ret = isdn_net_addphone(&phone)))
-				goto fault;
+			if ((ret = isdn_net_addphone(&phone))) {
+				restore_flags(flags);
+				return ret;
+			}
 			if (c2)
 				c = c2;
 			else
 				c += strlen(c);
 		}
-   		if(copy_from_user(buf, src, sizeof(buf)))
-			goto fault;
+		if ((ret = verify_area(VERIFY_READ, (void *) src, sizeof(buf)))) {
+			restore_flags(flags);
+			return ret;
+		}
+		copy_from_user(buf, src, sizeof(buf));
 		src += sizeof(buf);
 		c = buf;
 		while (*c) {
@@ -1129,8 +1257,10 @@
 			strcpy(phone.phone, c);
 			strcpy(phone.name, cfg.name);
 			phone.outgoing = 1;
-			if ((ret = isdn_net_addphone(&phone)))
-				goto out;
+			if ((ret = isdn_net_addphone(&phone))) {
+				restore_flags(flags);
+				return ret;
+			}
 			if (c2)
 				c = c2;
 			else
@@ -1138,27 +1268,28 @@
 		}
 		i--;
 	}
-out:
 	restore_flags(flags);
-	return ret;
-fault:
-	restore_flags(flags);
-	return -EFAULT;
+	return 0;
 }
 
-static int isdn_get_allcfg(char *dest)
+static int
+isdn_get_allcfg(char *dest)
 {
 	isdn_net_ioctl_cfg cfg;
 	isdn_net_ioctl_phone phone;
 	isdn_net_dev *p;
 	ulong flags;
-	int ret = 0;
+	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) {
@@ -1173,40 +1304,42 @@
 		cfg.p_encap = p->local.p_encap;
 		cfg.secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
 		cfg.callback = (p->local.flags & ISDN_NET_CALLBACK) ? 1 : 0;
-		cfg.chargehup = (p->local.hupflags & 4) ? 1 : 0;
-		cfg.ihup = (p->local.hupflags & 8) ? 1 : 0;
-		ret = 0;
-		ret += copy_to_user(dest, p->local.name, 10);
+		cfg.chargehup = (p->local.hupflags & ISDN_CHARGEHUP) ? 1 : 0;
+		cfg.ihup = (p->local.hupflags & ISDN_INHUP) ? 1 : 0;
+		cfg.chargeint = p->local.chargeint;
+		copy_to_user(dest, p->local.name, 10);
 		dest += 10;
-		ret += copy_to_user(dest, (char *) &cfg, sizeof(cfg));
+		copy_to_user(dest, (char *) &cfg, sizeof(cfg));
 		dest += sizeof(cfg);
 		strcpy(phone.name, p->local.name);
 		phone.outgoing = 0;
-		if (ret)
-			break;
-		if ((ret = isdn_net_getphones(&phone, dest)) < 0)
-			break;
-		else
+		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)
-			break;
-		else
+		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 ret;
+	return 0;
 }
 
-static int isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+static int
+isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 {
 	uint minor = MINOR(inode->i_rdev);
 	isdn_ctrl c;
 	int drvidx;
 	int chidx;
-	int ret = 0;
+	int ret;
 	char *s;
 	char name[10];
 	char bname[21];
@@ -1215,31 +1348,30 @@
 	isdn_net_ioctl_cfg cfg;
 
 	if (minor == ISDN_MINOR_STATUS) {
-                switch (cmd) {
-                        case IIOCGETDVR:
-                                return(TTY_DV +
-                                       (NET_DV << 8) +
-                                       (INF_DV << 16));
-                        case IIOCGETCPS:
-                                if (arg) {
-                                        ulong *p = (ulong *)arg;
-                                        int i;
-                                        for (i = 0;i<ISDN_MAX_CHANNELS;i++) {
-                                                ret = put_user(dev->ibytes[i],p);
-	                                            if (ret) break;
-												p++;
-												ret = put_user(dev->obytes[i],p);
-												p++;
-												if (ret) break;
-                                        }
-                                        return ret;
-                                } else
-                                        return -EINVAL;
-                                break;
-                        default:
-		                return -EINVAL;
-                }
-        }
+		switch (cmd) {
+			case IIOCGETDVR:
+				return (TTY_DV +
+					(NET_DV << 8) +
+					(INF_DV << 16));
+			case IIOCGETCPS:
+				if (arg) {
+					ulong *p = (ulong *) arg;
+					int i;
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+							       sizeof(ulong) * ISDN_MAX_CHANNELS * 2)))
+						return ret;
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						put_user(dev->ibytes[i], p++);
+						put_user(dev->obytes[i], p++);
+					}
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			default:
+				return -EINVAL;
+		}
+	}
 	if (!dev->drivers)
 		return -ENODEV;
 	if (minor < ISDN_MINOR_CTRL) {
@@ -1254,286 +1386,336 @@
 	if (minor <= ISDN_MINOR_CTRLMAX) {
 		switch (cmd) {
 #ifdef CONFIG_NETDEVICES
-                        case IIOCNETAIF:
-                                /* Add a network-interface */
-                                if (arg) {
-                                        if(copy_from_user(name, (char *) arg, sizeof(name)))
-											return -EFAULT;
-                                        s = name;
-                                } else
-                                        s = NULL;
-                                if ((s = isdn_net_new(s, NULL))) {
-									    return copy_to_user((char *) arg, s, strlen(s) + 1) ? -EFAULT : ret;
-                                } else
-                                        return -ENODEV;
-                        case IIOCNETASL:
-                                /* Add a slave to a network-interface */
-                                if (arg) {
-                                        if(copy_from_user(bname, (char *) arg, sizeof(bname)))
-											return -EFAULT;
-                                } else
-                                        return -EINVAL;
-                                if ((s = isdn_net_newslave(bname))) {
-                                        return copy_to_user((char *) arg, s, strlen(s) + 1) ? -EFAULT : 0;
-                                } else
-                                        return -ENODEV;
-                        case IIOCNETDIF:
-                                /* Delete a network-interface */
-                                if (arg) {
-                                        ret = copy_from_user(name, (char *) arg, sizeof(name));
-                                        return ret ? -EFAULT : isdn_net_rm(name);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETSCF:
-                                /* Set configurable parameters of a network-interface */
-                                if (arg) {
-                                        ret = copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg));
-                                        return ret ? -EFAULT : isdn_net_setcfg(&cfg);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETGCF:
-                                /* Get configurable parameters of a network-interface */
-                                if (arg) {
-                                        if(copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
-											return -EFAULT;
-                                        if (!(ret = isdn_net_getcfg(&cfg))) {
-                                                if(copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
-													return -EFAULT;
-                                        }
-                                        return ret;
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETANM:
-                                /* Add a phone-number to a network-interface */
-                                if (arg) {
-                                        ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
-                                        return ret ? -EFAULT : isdn_net_addphone(&phone);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETGNM:
-                                /* Get list of phone-numbers of a network-interface */
-                                if (arg) {
-                                        ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
-                                        return ret ? -EFAULT : isdn_net_getphones(&phone, (char *) arg);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETDNM:
-                                /* Delete a phone-number of a network-interface */
-                                if (arg) {
-                                        ret = copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
-                                        return ret ? -EFAULT : isdn_net_delphone(&phone);
-                                } else
-                                        return -EINVAL;
-                        case IIOCNETDIL:
-                                /* Force dialing of a network-interface */
-                                if (arg) {
-                                        ret = copy_from_user(name, (char *) arg, sizeof(name));
-                                        return ret ? -EFAULT : isdn_net_force_dial(name);
-                                } else
-                                        return -EINVAL;
+			case IIOCNETAIF:
+				/* Add a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+						return ret;
+					copy_from_user(name, (char *) arg, sizeof(name));
+					s = name;
+				} else
+					s = NULL;
+				if ((s = isdn_net_new(s, NULL))) {
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
+						return ret;
+					copy_to_user((char *) arg, s, strlen(s) + 1);
+					return 0;
+				} else
+					return -ENODEV;
+			case IIOCNETASL:
+				/* Add a slave to a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(bname))))
+						return ret;
+					copy_from_user(bname, (char *) arg, sizeof(bname));
+				} else
+					return -EINVAL;
+				if ((s = isdn_net_newslave(bname))) {
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg, strlen(s) + 1)))
+						return ret;
+					copy_to_user((char *) arg, s, strlen(s) + 1);
+					return 0;
+				} else
+					return -ENODEV;
+			case IIOCNETDIF:
+				/* Delete a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+						return ret;
+					copy_from_user(name, (char *) arg, sizeof(name));
+					return isdn_net_rm(name);
+				} else
+					return -EINVAL;
+			case IIOCNETSCF:
+				/* Set configurable parameters of a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
+						return ret;
+					copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg));
+					return isdn_net_setcfg(&cfg);
+				} else
+					return -EINVAL;
+			case IIOCNETGCF:
+				/* Get configurable parameters of a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(cfg))))
+						return ret;
+					copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg));
+					if (!(ret = isdn_net_getcfg(&cfg))) {
+						if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(cfg))))
+							return ret;
+						copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg));
+					}
+					return ret;
+				} else
+					return -EINVAL;
+			case IIOCNETANM:
+				/* Add a phone-number to a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+						return ret;
+					copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
+					return isdn_net_addphone(&phone);
+				} else
+					return -EINVAL;
+			case IIOCNETGNM:
+				/* Get list of phone-numbers of a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+						return ret;
+					copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
+					return isdn_net_getphones(&phone, (char *) arg);
+				} else
+					return -EINVAL;
+			case IIOCNETDNM:
+				/* Delete a phone-number of a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(phone))))
+						return ret;
+					copy_from_user((char *) &phone, (char *) arg, sizeof(phone));
+					return isdn_net_delphone(&phone);
+				} else
+					return -EINVAL;
+			case IIOCNETDIL:
+				/* Force dialing of a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+						return ret;
+					copy_from_user(name, (char *) arg, sizeof(name));
+					return isdn_net_force_dial(name);
+				} else
+					return -EINVAL;
 #ifdef CONFIG_ISDN_PPP
-                        case IIOCNETALN:
-								if (arg)
-                                        ret = copy_from_user(name,(char*)arg,sizeof(name));
-								else
-									    return -EINVAL;
-                                return ret ? -EFAULT : isdn_ppp_dial_slave(name);
-                        case IIOCNETDLN:
-
-                                if(arg) {
-									    ret = copy_from_user(name,(char*)arg,sizeof(name));
-                                } else
-                                        return -EINVAL;
-                                return ret ? -EFAULT : isdn_ppp_hangup_slave(name);
-#endif
-                        case IIOCNETHUP:
-                                /* Force hangup of a network-interface */
-                                if (arg) {
-                                        ret = copy_from_user(name, (char *) arg, sizeof(name));
-								        return ret ? -EFAULT : isdn_net_force_hangup(name);
-                                } else
-                                        return -EINVAL;
-                                break;
-#endif				/* CONFIG_NETDEVICES */
-                        case IIOCSETVER:
-                                dev->net_verbose = arg;
-                                printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
-                                return 0;
-                        case IIOCSETGST:
-                                if (arg)
-                                        dev->global_flags |= ISDN_GLOBAL_STOPPED;
-                                else
-                                        dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
-                                printk(KERN_INFO "isdn: Global Mode %s\n",
-                                       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
-                                return 0;
-                        case IIOCSETBRJ:
-                                drvidx = -1;
-                                if (arg) {
-                                        int i;
-                                        char *p;
-                                        if(copy_from_user((char *) &iocts, (char *) arg,
-                                                      sizeof(isdn_ioctl_struct)))
-											return -EFAULT;
-										if (strlen(iocts.drvid)) {
-                                                if ((p = strchr(iocts.drvid, ',')))
-                                                        *p = 0;
-                                                drvidx = -1;
-                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-                                                                drvidx = i;
-                                                                break;
-                                                        }
-                                        }
-                                }
-                                if (drvidx == -1)
-                                        return -ENODEV;
-                                dev->drv[drvidx]->reject_bus = iocts.arg;
-                                return 0;
-                        case IIOCGETSET:
-                                /* Get complete setup (all network-interfaces and profile-
-                                   settings of all tty-devices */
-                                if (arg)
-                                        return (isdn_get_allcfg((char *) arg));
-                                else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSETSET:
-                                /* Set complete setup (all network-interfaces and profile-
-                                   settings of all tty-devices */
-                                if (arg)
-                                        return (isdn_set_allcfg((char *) arg));
-                                else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSIGPRF:
-                                dev->profd = current;
-                                return 0;
-                                break;
-                        case IIOCGETPRF:
-                                /* Get all Modem-Profiles */
-                                if (arg) {
-                                        char *p = (char *) arg;
-                                        int i;
-
-                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                                                if(copy_to_user(p, dev->mdm.info[i].emu.profile,
-                                                            ISDN_MODEM_ANZREG))
-													return -EFAULT;
-                                                p += ISDN_MODEM_ANZREG;
-                                                if(copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
-													return -EFAULT;
-                                                p += ISDN_MSNLEN;
-                                        }
-                                        return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
-                                } else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSETPRF:
-                                /* Set all Modem-Profiles */
-                                if (arg) {
-                                        char *p = (char *) arg;
-                                        int i;
-
-                                        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                                                if(copy_from_user(dev->mdm.info[i].emu.profile, p,
-                                                              ISDN_MODEM_ANZREG))
-													return -EFAULT;
-												p += ISDN_MODEM_ANZREG;
-                                                if(copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))
-													return -EFAULT;
-												p += ISDN_MSNLEN;
-                                        }
-                                        return 0;
-                                } else
-                                        return -EINVAL;
-                                break;
-                        case IIOCSETMAP:
-                        case IIOCGETMAP:
-                                /* Set/Get MSN->EAZ-Mapping for a driver */
-                                if (arg) {
-                                        int i;
-                                        char *p;
-                                        char nstring[255];
-
-                                        ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
-
-										if (ret) return -EFAULT;
-										if (strlen(iocts.drvid)) {
-                                                drvidx = -1;
-                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-                                                                drvidx = i;
-                                                                break;
-                                                        }
-                                        } else
-                                                drvidx = 0;
-                                        if (drvidx == -1)
-                                                return -ENODEV;
-                                        if (cmd == IIOCSETMAP) {
-                                                ret = copy_from_user(nstring, (char *) iocts.arg, 255);
-												if (ret) return -EFAULT;
-                                                memset(dev->drv[drvidx]->msn2eaz, 0,
-                                                       sizeof(dev->drv[drvidx]->msn2eaz));
-                                                p = strtok(nstring, ",");
-                                                i = 0;
-                                                while ((p) && (i < 10)) {
-                                                        strcpy(dev->drv[drvidx]->msn2eaz[i++], p);
-                                                        p = strtok(NULL, ",");
-                                                }
-                                        } else {
-                                                p = nstring;
-                                                for (i = 0; i < 10; i++)
-                                                        p += sprintf(p, "%s%s",
-                                                                     strlen(dev->drv[drvidx]->msn2eaz[i]) ?
-                                                                     dev->drv[drvidx]->msn2eaz[i] : "-",
-                                                                     (i < 9) ? "," : "\0");
-												if(copy_to_user((char *) iocts.arg, nstring, strlen(nstring) + 1))
-													return -EFAULT;
-										}
-                                        return 0;
-                                } else
-                                        return -EINVAL;
-                        case IIOCDBGVAR:
-                                if (arg) {
-                                        return copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)) ? -EFAULT : 0;
-								} else
-                                        return -EINVAL;
-                                break;
-                        default:
-                                if ((cmd&IIOCDRVCTL)==IIOCDRVCTL)
-                                        cmd = ((cmd>>_IOC_NRSHIFT)&_IOC_NRMASK)& ISDN_DRVIOCTL_MASK;
+			case IIOCNETALN:
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ,
+							    (void *) arg,
+							  sizeof(name))))
+						return ret;
+				} else
+					return -EINVAL;
+				copy_from_user(name, (char *) arg, sizeof(name));
+				return isdn_ppp_dial_slave(name);
+			case IIOCNETDLN:
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ,
+							    (void *) arg,
+							  sizeof(name))))
+						return ret;
+				} else
+					return -EINVAL;
+				copy_from_user(name, (char *) arg, sizeof(name));
+				return isdn_ppp_hangup_slave(name);
+#endif
+			case IIOCNETHUP:
+				/* Force hangup of a network-interface */
+				if (arg) {
+					if ((ret = verify_area(VERIFY_READ, (void *) arg, sizeof(name))))
+						return ret;
+					copy_from_user(name, (char *) arg, sizeof(name));
+					return isdn_net_force_hangup(name);
+				} else
+					return -EINVAL;
+				break;
+#endif                          /* CONFIG_NETDEVICES */
+			case IIOCSETVER:
+				dev->net_verbose = arg;
+				printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+				return 0;
+			case IIOCSETGST:
+				if (arg)
+					dev->global_flags |= ISDN_GLOBAL_STOPPED;
+				else
+					dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+				printk(KERN_INFO "isdn: Global Mode %s\n",
+				       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+				return 0;
+			case IIOCSETBRJ:
+				drvidx = -1;
+				if (arg) {
+					int i;
+					char *p;
+					if ((ret = verify_area(VERIFY_READ, (void *) arg,
+					     sizeof(isdn_ioctl_struct))))
+						return ret;
+					copy_from_user((char *) &iocts, (char *) arg,
+					      sizeof(isdn_ioctl_struct));
+					if (strlen(iocts.drvid)) {
+						if ((p = strchr(iocts.drvid, ',')))
+							*p = 0;
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					}
+				}
+				if (drvidx == -1)
+					return -ENODEV;
+				dev->drv[drvidx]->reject_bus = iocts.arg;
+				return 0;
+			case IIOCGETSET:
+				/* Get complete setup (all network-interfaces and profile-
+				   settings of all tty-devices */
+				if (arg)
+					return (isdn_get_allcfg((char *) arg));
 				else
 					return -EINVAL;
-                                if (arg) {
-                                        int i;
-                                        char *p;
-
-                                        ret = copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
-										if (ret)
-											    return -EFAULT;
-                                        if (strlen(iocts.drvid)) {
-                                                if ((p = strchr(iocts.drvid, ',')))
-                                                        *p = 0;
-                                                drvidx = -1;
-                                                for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-                                                        if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-                                                                drvidx = i;
-                                                                break;
-                                                        }
-                                        } else
-                                                drvidx = 0;
-                                        if (drvidx == -1)
-                                                return -ENODEV;
-                                        c.driver = drvidx;
-                                        c.command = ISDN_CMD_IOCTL;
-                                        c.arg = cmd;
-                                        memcpy(c.num, (char *) &iocts.arg, sizeof(ulong));
-                                        ret = dev->drv[drvidx]->interface->command(&c);
-                                        memcpy((char *) &iocts.arg, c.num, sizeof(ulong));
-                                        return copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)) ? -EFAULT : 0;
-                                } else
-                                        return -EINVAL;
+				break;
+			case IIOCSETSET:
+				/* Set complete setup (all network-interfaces and profile-
+				   settings of all tty-devices */
+				if (arg)
+					return (isdn_set_allcfg((char *) arg));
+				else
+					return -EINVAL;
+				break;
+			case IIOCSIGPRF:
+				dev->profd = current;
+				return 0;
+				break;
+			case IIOCGETPRF:
+				/* Get all Modem-Profiles */
+				if (arg) {
+					char *p = (char *) arg;
+					int i;
+
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+						   * ISDN_MAX_CHANNELS)))
+						return ret;
+
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						copy_to_user(p, dev->mdm.info[i].emu.profile,
+						      ISDN_MODEM_ANZREG);
+						p += ISDN_MODEM_ANZREG;
+						copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN);
+						p += ISDN_MSNLEN;
+					}
+					return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+				} else
+					return -EINVAL;
+				break;
+			case IIOCSETPRF:
+				/* Set all Modem-Profiles */
+				if (arg) {
+					char *p = (char *) arg;
+					int i;
+
+					if ((ret = verify_area(VERIFY_READ, (void *) arg,
+					(ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+						   * ISDN_MAX_CHANNELS)))
+						return ret;
+
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						copy_from_user(dev->mdm.info[i].emu.profile, p,
+						      ISDN_MODEM_ANZREG);
+						p += ISDN_MODEM_ANZREG;
+						copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN);
+						p += ISDN_MSNLEN;
+					}
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			case IIOCSETMAP:
+			case IIOCGETMAP:
+				/* Set/Get MSN->EAZ-Mapping for a driver */
+				if (arg) {
+					int i;
+					char *p;
+					char nstring[255];
+
+					if ((ret = verify_area(VERIFY_READ, (void *) arg,
+					     sizeof(isdn_ioctl_struct))))
+						return ret;
+					copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+					if (strlen(iocts.drvid)) {
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					} else
+						drvidx = 0;
+					if (drvidx == -1)
+						return -ENODEV;
+					if (cmd == IIOCSETMAP) {
+						if ((ret = verify_area(VERIFY_READ, (void *) iocts.arg, 255)))
+							return ret;
+						copy_from_user(nstring, (char *) iocts.arg, 255);
+						memset(dev->drv[drvidx]->msn2eaz, 0,
+						       sizeof(dev->drv[drvidx]->msn2eaz));
+						p = strtok(nstring, ",");
+						i = 0;
+						while ((p) && (i < 10)) {
+							strcpy(dev->drv[drvidx]->msn2eaz[i++], p);
+							p = strtok(NULL, ",");
+						}
+					} else {
+						p = nstring;
+						for (i = 0; i < 10; i++)
+							p += sprintf(p, "%s%s",
+								     strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+								     dev->drv[drvidx]->msn2eaz[i] : "-",
+								     (i < 9) ? "," : "\0");
+						if ((ret = verify_area(VERIFY_WRITE, (void *) iocts.arg,
+						   strlen(nstring) + 1)))
+							return ret;
+						copy_to_user((char *) iocts.arg, nstring, strlen(nstring) + 1);
+					}
+					return 0;
+				} else
+					return -EINVAL;
+			case IIOCDBGVAR:
+				if (arg) {
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg, sizeof(ulong))))
+						return ret;
+					copy_to_user((char *) arg, (char *) &dev, sizeof(ulong));
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			default:
+				if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
+					cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
+				else
+					return -EINVAL;
+				if (arg) {
+					int i;
+					char *p;
+					if ((ret = verify_area(VERIFY_READ, (void *) arg,
+					     sizeof(isdn_ioctl_struct))))
+						return ret;
+					copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct));
+					if (strlen(iocts.drvid)) {
+						if ((p = strchr(iocts.drvid, ',')))
+							*p = 0;
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					} else
+						drvidx = 0;
+					if (drvidx == -1)
+						return -ENODEV;
+					if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+					     sizeof(isdn_ioctl_struct))))
+						return ret;
+					c.driver = drvidx;
+					c.command = ISDN_CMD_IOCTL;
+					c.arg = cmd;
+					memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong));
+					ret = dev->drv[drvidx]->interface->command(&c);
+					memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong));
+					copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct));
+					return ret;
+				} else
+					return -EINVAL;
 		}
 	}
 #ifdef CONFIG_ISDN_PPP
@@ -1548,7 +1730,8 @@
  * MOD_INC_USE_COUNT make sure that the driver memory is not freed
  * while the device is in use.
  */
-static int isdn_open(struct inode *ino, struct file *filep)
+static int
+isdn_open(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
 	int drvidx;
@@ -1600,20 +1783,21 @@
 	if (minor <= ISDN_MINOR_PPPMAX) {
 		int ret;
 		if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
-		        MOD_INC_USE_COUNT;
+			MOD_INC_USE_COUNT;
 		return ret;
 	}
 #endif
 	return -ENODEV;
 }
 
-static void isdn_close(struct inode *ino, struct file *filep)
+static void
+isdn_close(struct inode *ino, struct file *filep)
 {
 	uint minor = MINOR(ino->i_rdev);
 	int drvidx;
 	isdn_ctrl c;
 
-        MOD_DEC_USE_COUNT;
+	MOD_DEC_USE_COUNT;
 	if (minor == ISDN_MINOR_STATUS) {
 		infostruct *p = dev->infochain;
 		infostruct *q = NULL;
@@ -1629,7 +1813,7 @@
 			p = (infostruct *) (p->next);
 		}
 		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
-                return;
+		return;
 	}
 	if (minor < ISDN_MINOR_CTRL) {
 		drvidx = isdn_minor2drv(minor);
@@ -1662,17 +1846,21 @@
 	isdn_lseek,
 	isdn_read,
 	isdn_write,
-	NULL,			/* isdn_readdir */
-	isdn_select,		/* isdn_select */
-	isdn_ioctl,		/* isdn_ioctl */
-	NULL,			/* isdn_mmap */
+	NULL,                   /* isdn_readdir */
+#if (LINUX_VERSION_CODE < 0x020117)
+	isdn_select,            /* isdn_select */
+#else
+	isdn_poll,              /* isdn_poll */
+#endif
+	isdn_ioctl,             /* isdn_ioctl */
+	NULL,                   /* isdn_mmap */
 	isdn_open,
 	isdn_close,
-	NULL			/* fsync */
+	NULL                    /* fsync */
 };
 
 char *
- isdn_map_eaz2msn(char *msn, int di)
+isdn_map_eaz2msn(char *msn, int di)
 {
 	driver *this = dev->drv[di];
 	int i;
@@ -1690,13 +1878,14 @@
  * Find an unused ISDN-channel, whose feature-flags match the
  * given L2- and L3-protocols.
  */
-int isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
-		     ,int pre_chan)
+int
+isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+		      ,int pre_chan)
 {
 	int i;
 	ulong flags;
 	ulong features;
-        isdn_ctrl cmd;
+	isdn_ctrl cmd;
 
 	save_flags(flags);
 	cli();
@@ -1714,10 +1903,10 @@
 						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 						dev->usage[i] |= usage;
 						isdn_info_update();
-                                                cmd.driver = d;
-                                                cmd.arg = 0;
-                                                cmd.command = ISDN_CMD_LOCK;
-                                                (void) dev->drv[d]->interface->command(&cmd);
+						cmd.driver = d;
+						cmd.arg = 0;
+						cmd.command = ISDN_CMD_LOCK;
+						(void) dev->drv[d]->interface->command(&cmd);
 						restore_flags(flags);
 						return i;
 					} else {
@@ -1725,10 +1914,10 @@
 							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
 							dev->usage[i] |= usage;
 							isdn_info_update();
-                                                        cmd.driver = d;
-                                                        cmd.arg = 0;
-                                                        cmd.command = ISDN_CMD_LOCK;
-                                                        (void) dev->drv[d]->interface->command(&cmd);
+							cmd.driver = d;
+							cmd.arg = 0;
+							cmd.command = ISDN_CMD_LOCK;
+							(void) dev->drv[d]->interface->command(&cmd);
 							restore_flags(flags);
 							return i;
 						}
@@ -1743,7 +1932,8 @@
 /*
  * Set state of ISDN-channel to 'unused'
  */
-void isdn_free_channel(int di, int ch, int usage)
+void
+isdn_free_channel(int di, int ch, int usage)
 {
 	int i;
 	ulong flags;
@@ -1757,15 +1947,15 @@
 		    (dev->chanmap[i] == ch)) {
 			dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
 			strcpy(dev->num[i], "???");
-                        dev->ibytes[i] = 0;
-                        dev->obytes[i] = 0;
+			dev->ibytes[i] = 0;
+			dev->obytes[i] = 0;
 			isdn_info_update();
-                        isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
-                        cmd.driver = di;
-                        cmd.arg = ch;
-                        cmd.command = ISDN_CMD_UNLOCK;
-                        (void) dev->drv[di]->interface->command(&cmd);
-                        restore_flags(flags);
+			isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
+			cmd.driver = di;
+			cmd.arg = ch;
+			cmd.command = ISDN_CMD_UNLOCK;
+			restore_flags(flags);
+			(void) dev->drv[di]->interface->command(&cmd);
 			return;
 		}
 	restore_flags(flags);
@@ -1774,7 +1964,8 @@
 /*
  * Cancel Exclusive-Flag for ISDN-channel
  */
-void isdn_unexclusive_channel(int di, int ch)
+void
+isdn_unexclusive_channel(int di, int ch)
 {
 	int i;
 	ulong flags;
@@ -1802,58 +1993,57 @@
  * len     = Length of packet-data
  *
  */
-void isdn_receive_callback(int drvidx, int chan, u_char *buf, int len)
+static void
+isdn_receive_callback(int drvidx, int chan, u_char * buf, int len)
 {
-        struct sk_buff *skb;
+	struct sk_buff *skb;
 
 	if (dev->global_flags & ISDN_GLOBAL_STOPPED)
 		return;
-        skb = dev_alloc_skb(len);
-        if (skb) {
-                memcpy(skb_put(skb, len), buf, len);
-                isdn_receive_skb_callback(drvidx, chan, skb);
-        } else
-                printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n");
+	skb = dev_alloc_skb(len);
+	if (skb) {
+		memcpy(skb_put(skb, len), buf, len);
+		isdn_receive_skb_callback(drvidx, chan, skb);
+	} else
+		printk(KERN_WARNING "isdn: rcv alloc_skb failed, packet dropped.\n");
 }
 
 /*
  *  writebuf replacement for SKB_ABLE drivers
  */
-int isdn_writebuf_stub(int drvidx, int chan, const u_char *buf, int len,
-		       int user)
+static int
+isdn_writebuf_stub(int drvidx, int chan, const u_char * buf, int len,
+		   int user)
 {
 	int ret;
 
-        if (dev->drv[drvidx]->interface->writebuf)
-                ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf,
-                                                            len, user);
-        else {
-                struct sk_buff * skb;
-
-                skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len,
-                                GFP_ATOMIC);
-                if (skb == NULL)
-                        return 0;
-
-                skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
-                skb->free = 1;
-
-                if (user) {
-					    if(copy_from_user(skb_put(skb, len), buf, len)) {
-						        kfree_skb(skb,FREE_WRITE);
-						        return -EFAULT;
-						}
-                } else
-                        memcpy(skb_put(skb, len), buf, len);
+	if (dev->drv[drvidx]->interface->writebuf)
+		ret = dev->drv[drvidx]->interface->writebuf(drvidx, chan, buf,
+							    len, user);
+	else {
+		struct sk_buff *skb;
+
+		skb = alloc_skb(dev->drv[drvidx]->interface->hl_hdrlen + len,
+				GFP_ATOMIC);
+		if (skb == NULL)
+			return 0;
+
+		skb_reserve(skb, dev->drv[drvidx]->interface->hl_hdrlen);
+		SET_SKB_FREE(skb);
+
+		if (user)
+			copy_from_user(skb_put(skb, len), buf, len);
+		else
+			memcpy(skb_put(skb, len), buf, len);
 
-                ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx,
-                                                                chan, skb);
-                if (ret <= 0)
-                        kfree_skb(skb, FREE_WRITE);
-        }
-        if (ret > 0)
-                dev->obytes[isdn_dc2minor(drvidx,chan)] += ret;
-        return ret;
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx,
+							      chan, skb);
+		if (ret <= 0)
+			kfree_skb(skb, FREE_WRITE);
+	}
+	if (ret > 0)
+		dev->obytes[isdn_dc2minor(drvidx, chan)] += ret;
+	return ret;
 }
 
 /*
@@ -1864,32 +2054,36 @@
  * Return: length of data on success, -ERRcode on failure.
  */
 
-int isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff * skb)
+int
+isdn_writebuf_skb_stub(int drvidx, int chan, struct sk_buff *skb)
 {
-        int ret;
-	int len = skb->len;	/* skb pointer no longer valid after free */
+	int ret;
+	int len = skb->len;     /* skb pointer no longer valid after free */
 
-        if (dev->drv[drvidx]->interface->writebuf_skb)
+	if (dev->drv[drvidx]->interface->writebuf_skb)
 		ret = dev->drv[drvidx]->interface->
-			writebuf_skb(drvidx, chan, skb);
+		    writebuf_skb(drvidx, chan, skb);
 	else {
 		if ((ret = dev->drv[drvidx]->interface->
-                     writebuf(drvidx,chan,skb->data,skb->len,0)) == len)
-		        dev_kfree_skb(skb, FREE_WRITE);
-        }
-        if (ret > 0)
-                dev->obytes[isdn_dc2minor(drvidx,chan)] += len;
-        return ret;
+		  writebuf(drvidx, chan, skb->data, skb->len, 0)) == len)
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
+	if (ret > 0)
+		dev->obytes[isdn_dc2minor(drvidx, chan)] += len;
+	return ret;
 }
 
 /*
  * Low-level-driver registration
  */
 
-int register_isdn(isdn_if * i)
+int
+register_isdn(isdn_if * i)
 {
 	driver *d;
-	int n, j, k;
+	int n,
+	 j,
+	 k;
 	ulong flags;
 	int drvidx;
 
@@ -1905,9 +2099,9 @@
 		return 0;
 	}
 	if ((!i->writebuf_skb) && (!i->writebuf)) {
-                printk(KERN_WARNING "register_isdn: No write routine given.\n");
-                return 0;
-        }
+		printk(KERN_WARNING "register_isdn: No write routine given.\n");
+		return 0;
+	}
 	if (!(d = (driver *) kmalloc(sizeof(driver), GFP_KERNEL))) {
 		printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
 		return 0;
@@ -1926,17 +2120,17 @@
 		return 0;
 	}
 	memset((char *) d->rcvcount, 0, sizeof(int) * n);
-        if (!(d->rpqueue =
-              (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) {
-                printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
-                kfree(d->rcvcount);
-                kfree(d->rcverr);
-                kfree(d);
-                return 0;
-        }
-        for (j = 0; j < n; j++) {
-                skb_queue_head_init(&d->rpqueue[j]);
-        }
+	if (!(d->rpqueue =
+	      (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+		kfree(d->rcvcount);
+		kfree(d->rcverr);
+		kfree(d);
+		return 0;
+	}
+	for (j = 0; j < n; j++) {
+		skb_queue_head_init(&d->rpqueue[j]);
+	}
 	if (!(d->rcv_waitq = (struct wait_queue **)
 	      kmalloc(sizeof(struct wait_queue *) * n, GFP_KERNEL))) {
 		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
@@ -1972,15 +2166,15 @@
 	i->channels = drvidx;
 
 	i->rcvcallb_skb = isdn_receive_skb_callback;
-	i->rcvcallb     = isdn_receive_callback;
-	i->statcallb    = isdn_status_callback;
+	i->rcvcallb = isdn_receive_callback;
+	i->statcallb = isdn_status_callback;
 	if (!strlen(i->id))
 		sprintf(i->id, "line%d", drvidx);
 	save_flags(flags);
 	cli();
-        for (j = 0; j < drvidx; j++)
-                if (!strcmp(i->id,dev->drvid[j]))
-                    sprintf(i->id, "line%d", drvidx);
+	for (j = 0; j < drvidx; j++)
+		if (!strcmp(i->id, dev->drvid[j]))
+			sprintf(i->id, "line%d", drvidx);
 	for (j = 0; j < n; j++)
 		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
 			if (dev->chanmap[k] < 0) {
@@ -2009,7 +2203,8 @@
 #define isdn_init init_module
 #endif
 
-static char *isdn_getrev(const char *revision)
+static char *
+isdn_getrev(const char *revision)
 {
 	char *rev;
 	char *p;
@@ -2023,24 +2218,18 @@
 	return rev;
 }
 
-EXPORT_SYMBOL(register_isdn);
-
-static void isdn_export_syms(void)
-{
-        has_exported = 1;
-}
-
 /*
  * Allocate and initialize all data, register modem-devices
  */
-int isdn_init(void)
+int
+isdn_init(void)
 {
 	int i;
-        char irev[50];
-        char trev[50];
-        char nrev[50];
-        char prev[50];
-        char arev[50];
+	char irev[50];
+	char trev[50];
+	char nrev[50];
+	char prev[50];
+	char arev[50];
 
 	sti();
 	if (!(dev = (isdn_dev *) kmalloc(sizeof(isdn_dev), GFP_KERNEL))) {
@@ -2049,8 +2238,8 @@
 	}
 	memset((char *) dev, 0, sizeof(isdn_dev));
 #ifdef NEW_ISDN_TIMER_CTRL
-        init_timer(&dev->timer);
-        dev->timer.function = isdn_timer_funct;
+	init_timer(&dev->timer);
+	dev->timer.function = isdn_timer_funct;
 #endif
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		dev->drvmap[i] = -1;
@@ -2084,21 +2273,20 @@
 		kfree(dev);
 		return -EIO;
 	}
-#endif				/* CONFIG_ISDN_PPP */
+#endif                          /* CONFIG_ISDN_PPP */
 
-        if (!has_exported)
-                isdn_export_syms();
+	isdn_export_syms();
 
-        strcpy(irev,isdn_revision);
-        strcpy(trev,isdn_tty_revision);
-        strcpy(nrev,isdn_net_revision);
-        strcpy(prev,isdn_ppp_revision);
-        strcpy(arev,isdn_audio_revision);
+	strcpy(irev, isdn_revision);
+	strcpy(trev, isdn_tty_revision);
+	strcpy(nrev, isdn_net_revision);
+	strcpy(prev, isdn_ppp_revision);
+	strcpy(arev, isdn_audio_revision);
 	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(irev));
-        printk("%s/", isdn_getrev(trev));
-        printk("%s/", isdn_getrev(nrev));
-        printk("%s/", isdn_getrev(prev));
-        printk("%s", isdn_getrev(arev));
+	printk("%s/", isdn_getrev(trev));
+	printk("%s/", isdn_getrev(nrev));
+	printk("%s/", isdn_getrev(prev));
+	printk("%s", isdn_getrev(arev));
 
 #ifdef MODULE
 	printk(" loaded\n");
@@ -2114,7 +2302,8 @@
 /*
  * Unload module
  */
-void cleanup_module(void)
+void
+cleanup_module(void)
 {
 	int flags;
 	int i;
@@ -2140,9 +2329,9 @@
 		return;
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
-                kfree(dev->mdm.info[i].xmit_buf - 4);
-        }
+		isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
+		kfree(dev->mdm.info[i].xmit_buf - 4);
+	}
 	if (unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
 		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
 	} else {
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_common.h linux/drivers/isdn/isdn_common.h
--- v2.1.26/linux/drivers/isdn/isdn_common.h	Sat Jun  1 01:56:51 1996
+++ linux/drivers/isdn/isdn_common.h	Tue Feb 25 17:12:50 1997
@@ -1,11 +1,11 @@
-/* $Id: isdn_common.h,v 1.3 1996/05/19 00:13:05 fritz Exp $
- *
+/* $Id: isdn_common.h,v 1.5 1997/02/10 10:05:45 fritz Exp $
+
  * header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -18,9 +18,16 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_common.h,v $
+ * Revision 1.5  1997/02/10 10:05:45  fritz
+ * More changes for Kernel 2.1.X
+ * Symbol information moved to isdn_syms.c
+ *
+ * Revision 1.4  1997/02/03 22:56:50  fritz
+ * Removed isdn_writebuf_stub prototype.
+ *
  * Revision 1.3  1996/05/19 00:13:05  fritz
  * Removed debug flag.
  *
@@ -44,20 +51,25 @@
 #undef  ISDN_DEBUG_NET_ICALL
 
 /* Prototypes */
-extern void  isdn_MOD_INC_USE_COUNT(void);
-extern void  isdn_MOD_DEC_USE_COUNT(void);
-extern void  isdn_free_channel(int di, int ch, int usage);
-extern void  isdn_all_eaz(int di, int ch);
-extern int   isdn_dc2minor(int di, int ch);
-extern void  isdn_info_update(void);
-extern char* isdn_map_eaz2msn(char *msn, int di);
-extern void  isdn_timer_ctrl(int tf, int onoff);
-extern void  isdn_unexclusive_channel(int di, int ch);
-extern int   isdn_getnum(char **);
-extern int   isdn_readbchan (int, int, u_char *, u_char *, int, int);
-extern int   isdn_get_free_channel(int, int, int, int, int);
-extern int   isdn_writebuf_stub(int, int, const u_char *, int, int);
-extern int   isdn_writebuf_skb_stub(int, int, struct sk_buff *);
+extern void isdn_MOD_INC_USE_COUNT(void);
+extern void isdn_MOD_DEC_USE_COUNT(void);
+extern void isdn_free_channel(int di, int ch, int usage);
+extern void isdn_all_eaz(int di, int ch);
+extern int isdn_dc2minor(int di, int ch);
+extern void isdn_info_update(void);
+extern char *isdn_map_eaz2msn(char *msn, int di);
+extern void isdn_timer_ctrl(int tf, int onoff);
+extern void isdn_unexclusive_channel(int di, int ch);
+extern int isdn_getnum(char **);
+extern int isdn_readbchan(int, int, u_char *, u_char *, int, int);
+extern int isdn_get_free_channel(int, int, int, int, int);
+extern int isdn_writebuf_skb_stub(int, int, struct sk_buff *);
+extern int register_isdn(isdn_if * i);
+#if (LINUX_VERSION_CODE < 0x020111)
+extern void isdn_export_syms(void);
+#else
+#define isdn_export_syms()
+#endif
 #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
-extern void  isdn_dumppkt(char *, u_char *, int, int);
+extern void isdn_dumppkt(char *, u_char *, int, int);
 #endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v2.1.26/linux/drivers/isdn/isdn_net.c	Wed Oct 30 04:26:48 1996
+++ linux/drivers/isdn/isdn_net.c	Tue Feb 25 17:12:50 1997
@@ -1,11 +1,11 @@
-/* $Id: isdn_net.c,v 1.20 1996/08/29 20:06:03 fritz Exp $
- *
+/* $Id: isdn_net.c,v 1.37 1997/02/11 18:32:51 fritz Exp $
+
  * Linux ISDN subsystem, network interfaces and related functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -18,9 +18,67 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.c,v $
+ * Revision 1.37  1997/02/11 18:32:51  fritz
+ * Bugfix in isdn_ppp_free_mpqueue().
+ *
+ * Revision 1.36  1997/02/10 21:31:11  fritz
+ * Changed setup-interface (incoming and outgoing).
+ *
+ * Revision 1.35  1997/02/10 20:12:45  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.34  1997/02/03 23:15:07  fritz
+ * Reformatted according CodingStyle.
+ * replaced arp_find prototype by proper include.
+ * made dev_purge_queues static.
+ * Bugfix in bogocps calculation.
+ * removed isdn_net_receive_callback - was never used ;-)
+ * Misc. fixes for Kernel 2.1.X comaptibility.
+ *
+ * Revision 1.33  1997/01/17 01:19:25  fritz
+ * Applied chargeint patch.
+ *
+ * Revision 1.32  1997/01/14 01:29:31  fritz
+ * Bugfix: isdn_net_hangup() did not reset ISDN_NET_CONNECTED.
+ *
+ * Revision 1.31  1997/01/11 23:30:42  fritz
+ * Speed up dial statemachine.
+ *
+ * Revision 1.30  1996/11/25 17:20:50  hipp
+ * fixed pppbind bug in isdn_net_find_icall()
+ *
+ * Revision 1.29  1996/11/13 02:31:38  fritz
+ * Minor cleanup.
+ *
+ * Revision 1.28  1996/10/27 20:49:06  keil
+ * bugfix to compile without MPP
+ *
+ * Revision 1.27  1996/10/25 18:46:01  fritz
+ * Another bugfix in isdn_net_autohup()
+ *
+ * Revision 1.26  1996/10/23 23:05:36  fritz
+ * Bugfix: Divide by zero in isdn_net_autohup()
+ *
+ * Revision 1.25  1996/10/22 23:13:58  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
+ * Revision 1.24  1996/10/11 13:57:40  fritz
+ * Bugfix: Error in BogoCPS calculation.
+ *
+ * Revision 1.23  1996/09/23 01:58:08  fritz
+ * Fix: With syncPPP encapsulation, discard LCP packets
+ *      when calculating hangup timeout.
+ *
+ * Revision 1.22  1996/09/23 00:03:37  fritz
+ * Fix: did not compile without CONFIG_ISDN_PPP
+ *
+ * Revision 1.21  1996/09/07 12:44:50  hipp
+ * (hopefully) fixed callback problem with syncPPP
+ * syncPPP network devices now show PPP link encap
+ *
  * Revision 1.20  1996/08/29 20:06:03  fritz
  * Bugfix: Transmission timeout had been much to low.
  *
@@ -106,32 +164,27 @@
  *
  */
 
-#include <asm/uaccess.h>
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/isdn.h>
 #include <linux/if_arp.h>
+#include <net/arp.h>
 #include "isdn_common.h"
 #include "isdn_net.h"
 #ifdef CONFIG_ISDN_PPP
 #include "isdn_ppp.h"
 #endif
 
-/* In ksyms.c, but why not in some .h ??? */
-extern int arp_find(unsigned char *, u32, struct device *, u32,
-                    struct sk_buff *);
-
 /* Prototypes */
 
 int isdn_net_force_dial_lp(isdn_net_local *);
 static int isdn_net_wildmat(char *s, char *p);
 static int isdn_net_start_xmit(struct sk_buff *, struct device *);
 static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
- 
-extern void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
+static void dev_purge_queues(struct device *dev);	/* move this to net/core/dev.c */
 
-char *isdn_net_revision = "$Revision: 1.20 $";
+char *isdn_net_revision = "$Revision: 1.37 $";
 
  /*
   * Code for raw-networking over ISDN
@@ -143,7 +196,7 @@
 	ulong flags;
 
 	save_flags(flags);
-	cli();			/* Avoid glitch on writes to CMD regs */
+	cli();                  /* Avoid glitch on writes to CMD regs */
 	dev->interrupt = 0;
 	dev->tbusy = 0;
 	restore_flags(flags);
@@ -163,7 +216,7 @@
 		dev->dev_addr[i] = 0xfc;
 	memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(u32));
 
-        /* If this interface has slaves, start them also */
+	/* If this interface has slaves, start them also */
 
 	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
 		while (p) {
@@ -171,27 +224,26 @@
 			p->start = 1;
 			p = (((isdn_net_local *) p->priv)->slave);
 		}
- 	}
-
-	isdn_MOD_INC_USE_COUNT(); 
- 	return 0; 
-} 
+	}
+	isdn_MOD_INC_USE_COUNT();
+	return 0;
+}
 
-/* 
- Assign an ISDN-channel to a net-interface 
+/*
+ * Assign an ISDN-channel to a net-interface
  */
 static void
 isdn_net_bind_channel(isdn_net_local * lp, int idx)
 {
-        ulong flags;
+	ulong flags;
 
-        save_flags(flags);
-        cli();
+	save_flags(flags);
+	cli();
 	lp->isdn_device = dev->drvmap[idx];
 	lp->isdn_channel = dev->chanmap[idx];
-        dev->rx_netdev[idx] = lp->netdev;
-        dev->st_netdev[idx] = lp->netdev;
-        restore_flags(flags);
+	dev->rx_netdev[idx] = lp->netdev;
+	dev->st_netdev[idx] = lp->netdev;
+	restore_flags(flags);
 }
 
 /*
@@ -204,25 +256,25 @@
 
 	save_flags(flags);
 	cli();
-        if (lp->first_skb) {
-                dev_kfree_skb(lp->first_skb,FREE_WRITE);
-                lp->first_skb = NULL;
-        }
-	if(lp->sav_skb) {
-                dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+	if (lp->first_skb) {
+		dev_kfree_skb(lp->first_skb, FREE_WRITE);
+		lp->first_skb = NULL;
+	}
+	if (lp->sav_skb) {
+		dev_kfree_skb(lp->sav_skb, FREE_WRITE);
 		lp->sav_skb = NULL;
 	}
-	if(!lp->master) /* purge only for master device */
+	if (!lp->master)        /* purge only for master device */
 		dev_purge_queues(&lp->netdev->dev);
 	lp->dialstate = 0;
-	dev->rx_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
-	dev->st_netdev[isdn_dc2minor(lp->isdn_device,lp->isdn_channel)] = NULL;
+	dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+	dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
 	isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
 	lp->flags &= ~ISDN_NET_CONNECTED;
 	lp->isdn_device = -1;
 	lp->isdn_channel = -1;
 
-        restore_flags(flags);
+	restore_flags(flags);
 }
 
 /*
@@ -238,41 +290,56 @@
  * Since this function is called every second, simply reset the
  * byte-counter of the interface after copying it to the cps-variable.
  */
+unsigned long last_jiffies = -HZ;
+
 void
 isdn_net_autohup()
 {
 	isdn_net_dev *p = dev->netdev;
-        int anymore;
-	ulong flags;
+	int anymore;
 
-	save_flags(flags);
-	cli();
-        anymore = 0;
+	anymore = 0;
 	while (p) {
 		isdn_net_local *l = (isdn_net_local *) & (p->local);
-		l->cps = l->transcount;
+		if ((jiffies - last_jiffies) == 0)
+			l->cps = l->transcount;
+		else
+			l->cps = (l->transcount * HZ) / (jiffies - last_jiffies);
 		l->transcount = 0;
 		if (dev->net_verbose > 3)
 			printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
 		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
-                        anymore = 1;
+			anymore = 1;
 			l->huptimer++;
 			if ((l->onhtime) && (l->huptimer > l->onhtime))
-				if (l->outgoing) {
-					if (l->hupflags & 4) {
-						if (l->hupflags & 1)
+				if (l->hupflags & ISDN_MANCHARGE &&
+				    l->hupflags & ISDN_CHARGEHUP) {
+					while (jiffies - l->chargetime > l->chargeint)
+						l->chargetime += l->chargeint;
+					if (jiffies - l->chargetime >= l->chargeint - 2 * HZ)
+						if (l->outgoing || l->hupflags & ISDN_INHUP)
 							isdn_net_hangup(&p->dev);
-						else if (jiffies - l->chargetime > l->chargeint)
+				} else if (l->outgoing) {
+					if (l->hupflags & ISDN_CHARGEHUP) {
+						if (l->hupflags & ISDN_WAITCHARGE) {
+							printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n",
+							       l->name, l->hupflags);
 							isdn_net_hangup(&p->dev);
+						} else if (jiffies - l->chargetime > l->chargeint) {
+							printk(KERN_DEBUG
+							       "isdn_net: %s: chtime = %d, chint = %d\n",
+							       l->name, l->chargetime, l->chargeint);
+							isdn_net_hangup(&p->dev);
+						}
 					} else
 						isdn_net_hangup(&p->dev);
-				} else if (l->hupflags & 8)
+				} else if (l->hupflags & ISDN_INHUP)
 					isdn_net_hangup(&p->dev);
 		}
 		p = (isdn_net_dev *) p->next;
 	}
-        isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,anymore);
-	restore_flags(flags);
+	last_jiffies = jiffies;
+	isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);
 }
 
 /*
@@ -288,57 +355,56 @@
 
 	if (p) {
 		isdn_net_local *lp = &(p->local);
-                switch (cmd) {
+		switch (cmd) {
 			case ISDN_STAT_BSENT:
 				/* A packet has successfully been sent out */
 				if ((lp->flags & ISDN_NET_CONNECTED) &&
 				    (!lp->dialstate)) {
 					lp->stats.tx_packets++;
-					if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) {
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && lp->sav_skb) {
 						struct device *mdev;
-						if(lp->master)
+						if (lp->master)
 							mdev = lp->master;
 						else
 							mdev = &lp->netdev->dev;
-						if(!isdn_net_send_skb(mdev,lp,lp->sav_skb)) {
+						if (!isdn_net_send_skb(mdev, lp, lp->sav_skb)) {
 							lp->sav_skb = NULL;
 							mark_bh(NET_BH);
-						}
-						else {
+						} else {
 							return 1;
 						}
 					}
-                                        if (clear_bit(0,(void*)&(p->dev.tbusy)))
-                                                mark_bh(NET_BH);
+					if (clear_bit(0, (void *) &(p->dev.tbusy)))
+						mark_bh(NET_BH);
 				}
 				return 1;
 			case ISDN_STAT_DCONN:
 				/* D-Channel is up */
-                                switch (lp->dialstate) {
-                                        case 4:
-                                        case 7:
-                                        case 8:
-                                                lp->dialstate++;
-                                                return 1;
-                                        case 12:
-                                                lp->dialstate = 5;
-                                                return 1;
-                                }
+				switch (lp->dialstate) {
+					case 4:
+					case 7:
+					case 8:
+						lp->dialstate++;
+						return 1;
+					case 12:
+						lp->dialstate = 5;
+						return 1;
+				}
 				break;
 			case ISDN_STAT_DHUP:
 				/* Either D-Channel-hangup or error during dialout */
 				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
 					lp->flags &= ~ISDN_NET_CONNECTED;
-					if(lp->first_skb) {
-						dev_kfree_skb(lp->first_skb,FREE_WRITE);
+					if (lp->first_skb) {
+						dev_kfree_skb(lp->first_skb, FREE_WRITE);
 						lp->first_skb = NULL;
 					}
-					if(lp->sav_skb) {
-						dev_kfree_skb(lp->sav_skb,FREE_WRITE);
+					if (lp->sav_skb) {
+						dev_kfree_skb(lp->sav_skb, FREE_WRITE);
 						lp->sav_skb = NULL;
 					}
 					isdn_free_channel(lp->isdn_device, lp->isdn_channel,
-                                                          ISDN_USAGE_NET);
+							  ISDN_USAGE_NET);
 #ifdef CONFIG_ISDN_PPP
 					isdn_ppp_free(lp);
 #endif
@@ -350,49 +416,51 @@
 					lp->isdn_channel = -1;
 					dev->st_netdev[idx] = NULL;
 					dev->rx_netdev[idx] = NULL;
-                                        return 1;
+					return 1;
 				}
-                                break;
+				break;
 			case ISDN_STAT_BCONN:
 				/* B-Channel is up */
-                                switch (lp->dialstate) {
-                                        case 5:
-                                        case 6:
-                                        case 7:
-                                        case 8:
-                                        case 9:
-                                        case 10:
-                                        case 12:
-                                                if (lp->dialstate <= 6) {
-                                                        dev->usage[idx] |= ISDN_USAGE_OUTGOING;
-                                                        isdn_info_update();
-                                                } else
-                                                        dev->rx_netdev[idx] = p;
-                                                lp->dialstate = 0;
-                                                isdn_timer_ctrl(ISDN_TIMER_NETHANGUP,1);
-                                                printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
-                                                /* If first Chargeinfo comes before B-Channel connect,
-                                                 * we correct the timestamp here.
-                                                 */
-                                                lp->chargetime = jiffies;
-                                                /* Immediately send first skb to speed up arp */
+				switch (lp->dialstate) {
+					case 5:
+					case 6:
+					case 7:
+					case 8:
+					case 9:
+					case 10:
+					case 12:
+						if (lp->dialstate <= 6) {
+							dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+							isdn_info_update();
+						} else
+							dev->rx_netdev[idx] = p;
+						lp->dialstate = 0;
+						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
+						printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
+						/* If first Chargeinfo comes before B-Channel connect,
+						 * we correct the timestamp here.
+						 */
+						lp->chargetime = jiffies;
+						printk(KERN_DEBUG "isdn_net: chargetime of %s now %d\n",
+						lp->name, lp->chargetime);
+						/* Immediately send first skb to speed up arp */
 #ifdef CONFIG_ISDN_PPP
-						if(lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 							isdn_ppp_wakeup_daemon(lp);
 #endif
-                                                if (lp->first_skb) {
-                                                        if (!(isdn_net_xmit(&p->dev,lp,lp->first_skb)))
-                                                                lp->first_skb = NULL;
-                                                }
-                                                return 1;
+						if (lp->first_skb) {
+							if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
+								lp->first_skb = NULL;
+						}
+						return 1;
 				}
 				break;
 			case ISDN_STAT_NODCH:
 				/* No D-Channel avail. */
 				if (lp->dialstate == 4) {
 					lp->dialstate--;
-                                        return 1;
-                                }
+					return 1;
+				}
 				break;
 			case ISDN_STAT_CINF:
 				/* Charge-info from TelCo. Calculate interval between
@@ -400,17 +468,19 @@
 				 * usage by isdn_net_autohup()
 				 */
 				lp->charge++;
-				if (lp->hupflags & 2) {
-					lp->hupflags &= ~1;
+				if (lp->hupflags & ISDN_HAVECHARGE) {
+					lp->hupflags &= ~ISDN_WAITCHARGE;
 					lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
 				}
-				if (lp->hupflags & 1)
-					lp->hupflags |= 2;
+				if (lp->hupflags & ISDN_WAITCHARGE)
+					lp->hupflags |= ISDN_HAVECHARGE;
 				lp->chargetime = jiffies;
+				printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %d\n",
+				       lp->name, lp->chargetime);
 				return 1;
-                }
+		}
 	}
-        return 0;
+	return 0;
 }
 
 /*
@@ -447,187 +517,195 @@
 
 	while (p) {
 #ifdef ISDN_DEBUG_NET_DIAL
-                if (p->local.dialstate)
-                        printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name,p->local.dialstate);
+		if (p->local.dialstate)
+			printk(KERN_DEBUG "%s: dialstate=%d\n", p->local.name, p->local.dialstate);
 #endif
 		switch (p->local.dialstate) {
-		case 0:
-			/* Nothing to do for this interface */
-			break;
-		case 1:
-			/* Initiate dialout. Set phone-number-pointer to first number
-			 * of interface.
-			 */
-			p->local.dial = p->local.phone[1];
-			anymore = 1;
-			p->local.dialstate++;
-			break;
-			/* Prepare dialing. Clear EAZ, then set EAZ. */
-		case 2:
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			cmd.command = ISDN_CMD_CLREAZ;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			sprintf(cmd.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
-			cmd.command = ISDN_CMD_SETEAZ;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			p->local.dialretry = 0;
-			anymore = 1;
-			p->local.dialstate++;
-			break;
-		case 3:
-			/* Setup interface, dial current phone-number, switch to next number.
-			 * If list of phone-numbers is exhausted, increment
-			 * retry-counter.
-			 */
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL2;
-			cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL3;
-			cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			p->local.huptimer = 0;
-			p->local.outgoing = 1;
-			p->local.hupflags |= 1;
-                        p->local.hupflags &= ~2;
-			if (!strcmp(p->local.dial->num, "LEASED")) {
-				p->local.dialstate = 4;
-				printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
-			} else {
-				cmd.command = ISDN_CMD_DIAL;
-				sprintf(cmd.num, "%s,%s,7,0", p->local.dial->num,
-				  isdn_map_eaz2msn(p->local.msn, cmd.driver));
-				i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
-				if (i >= 0) {
-					strcpy(dev->num[i], p->local.dial->num);
-					isdn_info_update();
-				}
-				printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
-				 p->local.dialretry, p->local.dial->num);
-				/*
-				 * Switch to next number or back to start if at end of list.
+			case 0:
+				/* Nothing to do for this interface */
+				break;
+			case 1:
+				/* Initiate dialout. Set phone-number-pointer to first number
+				 * of interface.
+				 */
+				p->local.dial = p->local.phone[1];
+				anymore = 1;
+				p->local.dialstate++;
+				/* Fall through */
+			case 2:
+				/* Prepare dialing. Clear EAZ, then set EAZ. */
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				cmd.command = ISDN_CMD_CLREAZ;
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(p->local.msn, cmd.driver));
+				cmd.command = ISDN_CMD_SETEAZ;
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				p->local.dialretry = 0;
+				anymore = 1;
+				p->local.dialstate++;
+				/* Falls through */
+			case 3:
+				/* Setup interface, dial current phone-number, switch to next number.
+				 * If list of phone-numbers is exhausted, increment
+				 * retry-counter.
 				 */
-				if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
-					p->local.dial = p->local.phone[1];
-					p->local.dialretry++;
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				p->local.huptimer = 0;
+				p->local.outgoing = 1;
+				if (p->local.chargeint) {
+					p->local.hupflags |= ISDN_HAVECHARGE;
+					p->local.hupflags &= ~ISDN_WAITCHARGE;
+				} else {
+					p->local.hupflags |= ISDN_WAITCHARGE;
+					p->local.hupflags &= ~ISDN_HAVECHARGE;
 				}
-				p->local.dtimer = 0;
+				if (!strcmp(p->local.dial->num, "LEASED")) {
+					p->local.dialstate = 4;
+					printk(KERN_INFO "%s: Open leased line ...\n", p->local.name);
+				} else {
+					cmd.command = ISDN_CMD_DIAL;
+					cmd.parm.setup.si1 = 7;
+					cmd.parm.setup.si2 = 0;
+					sprintf(cmd.parm.setup.phone, "%s", p->local.dial->num);
+					sprintf(cmd.parm.setup.eazmsn, "%s",
+						isdn_map_eaz2msn(p->local.msn, cmd.driver));
+					i = isdn_dc2minor(p->local.isdn_device, p->local.isdn_channel);
+					if (i >= 0) {
+						strcpy(dev->num[i], p->local.dial->num);
+						isdn_info_update();
+					}
+					printk(KERN_INFO "%s: dialing %d %s...\n", p->local.name,
+					       p->local.dialretry, p->local.dial->num);
+					/*
+					 * Switch to next number or back to start if at end of list.
+					 */
+					if (!(p->local.dial = (isdn_net_phone *) p->local.dial->next)) {
+						p->local.dial = p->local.phone[1];
+						p->local.dialretry++;
+					}
+					p->local.dtimer = 0;
 #ifdef ISDN_DEBUG_NET_DIAL
-				printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
-				       p->local.isdn_channel);
+					printk(KERN_DEBUG "dial: d=%d c=%d\n", p->local.isdn_device,
+					       p->local.isdn_channel);
 #endif
+					dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				}
+				anymore = 1;
+				p->local.dialstate =
+				    (p->local.cbdelay &&
+				     (p->local.flags & ISDN_NET_CBOUT)) ? 12 : 4;
+				break;
+			case 4:
+				/* Wait for D-Channel-connect.
+				 * If timeout and max retries not
+				 * reached, switch back to state 3.
+				 */
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					if (p->local.dialretry < p->local.dialmax) {
+						p->local.dialstate = 3;
+					} else
+						isdn_net_hangup(&p->dev);
+				anymore = 1;
+				break;
+			case 5:
+				/* Got D-Channel-Connect, send B-Channel-request */
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				anymore = 1;
+				p->local.dtimer = 0;
+				p->local.dialstate++;
 				dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			}
-			anymore = 1;
-			p->local.dialstate =
-                                (p->local.cbdelay &&
-                                 (p->local.flags & ISDN_NET_CBOUT))?12:4;
-			break;
-		case 4:
-			/* Wait for D-Channel-connect.
-                         * If timeout and max retries not
-			 * reached, switch back to state 3.
-			 */
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-				if (p->local.dialretry < p->local.dialmax) {
-					p->local.dialstate = 3;
-				} else
-                                        isdn_net_hangup(&p->dev);
-			anymore = 1;
-			break;
-		case 5:
-			/* Got D-Channel-Connect, send B-Channel-request */
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			cmd.command = ISDN_CMD_ACCEPTB;
-			anymore = 1;
-			p->local.dtimer = 0;
-			p->local.dialstate++;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			break;
-		case 6:
-			/* Wait for B- or D-Channel-connect. If timeout,
-                         * switch back to state 3.
-			 */
+				break;
+			case 6:
+				/* Wait for B- or D-Channel-connect. If timeout,
+				 * switch back to state 3.
+				 */
 #ifdef ISDN_DEBUG_NET_DIAL
-			printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
+				printk(KERN_DEBUG "dialtimer2: %d\n", p->local.dtimer);
 #endif
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-				p->local.dialstate = 3;
-			anymore = 1;
-			break;
-		case 7:
-			/* Got incoming Call, setup L2 and L3 protocols,
-                         * then wait for D-Channel-connect
-                         */
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					p->local.dialstate = 3;
+				anymore = 1;
+				break;
+			case 7:
+				/* Got incoming Call, setup L2 and L3 protocols,
+				 * then wait for D-Channel-connect
+				 */
 #ifdef ISDN_DEBUG_NET_DIAL
-			printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+				printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
 #endif
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL2;
-			cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			cmd.driver = p->local.isdn_device;
-			cmd.command = ISDN_CMD_SETL3;
-			cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
-				isdn_net_hangup(&p->dev);
-			else {
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = p->local.isdn_channel + (p->local.l2_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				cmd.driver = p->local.isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = p->local.isdn_channel + (p->local.l3_proto << 8);
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT15)
+					isdn_net_hangup(&p->dev);
+				else {
+					anymore = 1;
+					p->local.dialstate++;
+				}
+				break;
+			case 9:
+				/* Got incoming D-Channel-Connect, send B-Channel-request */
+				cmd.driver = p->local.isdn_device;
+				cmd.arg = p->local.isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				dev->drv[p->local.isdn_device]->interface->command(&cmd);
 				anymore = 1;
-                                p->local.dialstate++;
-                        }
-			break;
-		case 9:
-			/* Got incoming D-Channel-Connect, send B-Channel-request */
-			cmd.driver = p->local.isdn_device;
-			cmd.arg = p->local.isdn_channel;
-			cmd.command = ISDN_CMD_ACCEPTB;
-			dev->drv[p->local.isdn_device]->interface->command(&cmd);
-			anymore = 1;
-			p->local.dtimer = 0;
-			p->local.dialstate++;
-			break;
-		case 8:
-                case 10:
-			/*  Wait for B- or D-channel-connect */
+				p->local.dtimer = 0;
+				p->local.dialstate++;
+				break;
+			case 8:
+			case 10:
+				/*  Wait for B- or D-channel-connect */
 #ifdef ISDN_DEBUG_NET_DIAL
-			printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
+				printk(KERN_DEBUG "dialtimer4: %d\n", p->local.dtimer);
 #endif
-			if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
-				isdn_net_hangup(&p->dev);
-			else
+				if (p->local.dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					isdn_net_hangup(&p->dev);
+				else
+					anymore = 1;
+				break;
+			case 11:
+				/* Callback Delay */
+				if (p->local.dtimer++ > p->local.cbdelay)
+					p->local.dialstate = 1;
 				anymore = 1;
-			break;
-                case 11:
-                        /* Callback Delay */
-                        if (p->local.dtimer++ > p->local.cbdelay)
-                                p->local.dialstate = 1;
-                        anymore = 1;
-                        break;
-		case 12:
-			/* Remote does callback. Hangup after cbdelay, then wait for incoming
-                         * call (in state 4).
-			 */
-                        if (p->local.dtimer++ > p->local.cbdelay) {
-                                printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name);
-                                p->local.dtimer = 0;
-                                p->local.dialstate = 4;
-                                cmd.driver = p->local.isdn_device;
-                                cmd.command = ISDN_CMD_HANGUP;
-                                cmd.arg = p->local.isdn_channel;
-                                (void) dev->drv[cmd.driver]->interface->command(&cmd);
-                                isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
-                        }
-                        anymore = 1;
-			break;
-		default:
-			printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
-			       p->local.dialstate, p->local.name);
+				break;
+			case 12:
+				/* Remote does callback. Hangup after cbdelay, then wait for incoming
+				 * call (in state 4).
+				 */
+				if (p->local.dtimer++ > p->local.cbdelay) {
+					printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->local.name);
+					p->local.dtimer = 0;
+					p->local.dialstate = 4;
+					cmd.driver = p->local.isdn_device;
+					cmd.command = ISDN_CMD_HANGUP;
+					cmd.arg = p->local.isdn_channel;
+					(void) dev->drv[cmd.driver]->interface->command(&cmd);
+					isdn_all_eaz(p->local.isdn_device, p->local.isdn_channel);
+				}
+				anymore = 1;
+				break;
+			default:
+				printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+				       p->local.dialstate, p->local.name);
 		}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -644,6 +722,7 @@
 	isdn_ctrl cmd;
 
 	if (lp->flags & ISDN_NET_CONNECTED) {
+		lp->flags &= ~ISDN_NET_CONNECTED;
 		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
 #ifdef CONFIG_ISDN_PPP
 		isdn_ppp_free(lp);
@@ -655,7 +734,7 @@
 		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
 		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
 	}
-        isdn_net_unbind_channel(lp);
+	isdn_net_unbind_channel(lp);
 }
 
 typedef struct {
@@ -666,59 +745,59 @@
 static void
 isdn_net_log_packet(u_char * buf, isdn_net_local * lp)
 {
-        u_char *p = buf;
+	u_char *p = buf;
 	unsigned short proto = ETH_P_IP;
-        int data_ofs;
+	int data_ofs;
 	ip_ports *ipp;
 	char addinfo[100];
 
-        addinfo[0] = '\0';
-        switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_IPTYP:
-                        proto = ntohs(*(unsigned short *)&buf[0]);
-                        p = &buf[2];
-                        break;
-                case ISDN_NET_ENCAP_ETHER:
-                        proto = ntohs(*(unsigned short *)&buf[12]);
-                        p = &buf[14];
-                        break;
-                case ISDN_NET_ENCAP_CISCOHDLC:
-                        proto = ntohs(*(unsigned short *)&buf[2]);
-                        p = &buf[4];
-                        break;
-        }
+	addinfo[0] = '\0';
+	switch (lp->p_encap) {
+		case ISDN_NET_ENCAP_IPTYP:
+			proto = ntohs(*(unsigned short *) &buf[0]);
+			p = &buf[2];
+			break;
+		case ISDN_NET_ENCAP_ETHER:
+			proto = ntohs(*(unsigned short *) &buf[12]);
+			p = &buf[14];
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+			proto = ntohs(*(unsigned short *) &buf[2]);
+			p = &buf[4];
+			break;
+	}
 	data_ofs = ((p[0] & 15) * 4);
-        switch (proto) {
+	switch (proto) {
 		case ETH_P_IP:
 			switch (p[9]) {
-                                case 1:
-                                        strcpy(addinfo, " ICMP");
-                                        break;
-                                case 2:
-                                        strcpy(addinfo, " IGMP");
-                                        break;
-                                case 4:
-                                        strcpy(addinfo, " IPIP");
-                                        break;
-                                case 6:
-                                        ipp = (ip_ports *) (&p[data_ofs]);
-                                        sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
-                                                ntohs(ipp->dest));
-                                        break;
-                                case 8:
-                                        strcpy(addinfo, " EGP");
-                                        break;
-                                case 12:
-                                        strcpy(addinfo, " PUP");
-                                        break;
-                                case 17:
-                                        ipp = (ip_ports *) (&p[data_ofs]);
-                                        sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
-                                                ntohs(ipp->dest));
-                                        break;
-                                case 22:
-                                        strcpy(addinfo, " IDP");
-                                        break;
+				case 1:
+					strcpy(addinfo, " ICMP");
+					break;
+				case 2:
+					strcpy(addinfo, " IGMP");
+					break;
+				case 4:
+					strcpy(addinfo, " IPIP");
+					break;
+				case 6:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 8:
+					strcpy(addinfo, " EGP");
+					break;
+				case 12:
+					strcpy(addinfo, " PUP");
+					break;
+				case 17:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 22:
+					strcpy(addinfo, " IDP");
+					break;
 			}
 			printk(KERN_INFO "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
 			       p[12], p[13], p[14], p[15],
@@ -730,7 +809,7 @@
 			       p[14], p[15], p[16], p[17],
 			       p[24], p[25], p[26], p[27]);
 			break;
-        }
+	}
 }
 
 /*
@@ -742,28 +821,28 @@
  * Side-effects: ndev->tbusy is cleared on success.
  */
 int
-isdn_net_send_skb(struct device *ndev, isdn_net_local *lp,
-                  struct sk_buff *skb)
+isdn_net_send_skb(struct device *ndev, isdn_net_local * lp,
+		  struct sk_buff *skb)
 {
 	int ret;
-	int len = skb->len;	/* save len */
-	
-        ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb);
+	int len = skb->len;     /* save len */
+
+	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb);
 	if (ret == len) {
 		lp->transcount += len;
-		clear_bit(0, (void *)&(ndev->tbusy));
+		clear_bit(0, (void *) &(ndev->tbusy));
+		return 0;
+	}
+	if (ret < 0) {
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, FREE_WRITE);
+		lp->stats.tx_errors++;
+		clear_bit(0, (void *) &(ndev->tbusy));
 		return 0;
 	}
-        if (ret < 0) {
-                skb->free = 1;
-                dev_kfree_skb(skb, FREE_WRITE);
-                lp->stats.tx_errors++;
-                clear_bit(0, (void *)&(ndev->tbusy));
-                return 0;
-        }
 	return 1;
-}                                      
-	
+}
+
 
 /*
  *  Helper function for isdn_net_start_xmit.
@@ -777,23 +856,23 @@
  */
 
 static int
-isdn_net_xmit(struct device *ndev, isdn_net_local *lp, struct sk_buff *skb) 
+isdn_net_xmit(struct device *ndev, isdn_net_local * lp, struct sk_buff *skb)
 {
-        int ret;
+	int ret;
 
 	/* For the other encaps the header has already been built */
 #ifdef CONFIG_ISDN_PPP
 	if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
 		return isdn_ppp_xmit(skb, ndev);
 	}
-#endif		
+#endif
 	/* Reset hangup-timeout */
 	lp->huptimer = 0;
 	if (lp->cps > 7000) {
 		/* Device overloaded */
 
-		/* 
-		 * Packet-delivery via round-robin over master 
+		/*
+		 * Packet-delivery via round-robin over master
 		 * and all connected slaves.
 		 */
 		if (lp->master)
@@ -817,18 +896,16 @@
 				/* First time overload: set timestamp only */
 				lp->sqfull = 1;
 				lp->sqfull_stamp = jiffies;
-			} 
-			else {
+			} else {
 				/* subsequent overload: if slavedelay exceeded, start dialing */
 				if ((jiffies - lp->sqfull_stamp) > lp->slavedelay)
 					isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
 			}
 		}
-	} 
-	else {
+	} else {
 		/* Not overloaded, deliver locally */
 		ret = isdn_net_send_skb(ndev, lp, skb);
-		if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10*HZ) )))
+		if (lp->sqfull && ((jiffies - lp->sqfull_stamp) > (lp->slavedelay + (10 * HZ))))
 			lp->sqfull = 0;
 	}
 	return ret;
@@ -847,8 +924,8 @@
 	if (ndev->tbusy) {
 		if (jiffies - ndev->trans_start < (2 * HZ))
 			return 1;
-                if (!lp->dialstate)
-                        lp->stats.tx_errors++;
+		if (!lp->dialstate)
+			lp->stats.tx_errors++;
 		ndev->tbusy = 0;
 		ndev->trans_start = jiffies;
 	}
@@ -859,8 +936,8 @@
 	/* Avoid timer-based retransmission conflicts. */
 	if (set_bit(0, (void *) &ndev->tbusy) != 0)
 		printk(KERN_WARNING
-                       "%s: Transmitter access conflict.\n",
-                       ndev->name);
+		       "%s: Transmitter access conflict.\n",
+		       ndev->name);
 	else {
 		u_char *buf = skb->data;
 #ifdef ISDN_DEBUG_NET_DUMP
@@ -873,23 +950,23 @@
 				save_flags(flags);
 				cli();
 				/* Grab a free ISDN-Channel */
-				if ((chi = 
-                                     isdn_get_free_channel(ISDN_USAGE_NET,
-                                                           lp->l2_proto,
-                                                           lp->l3_proto,
-                                                           lp->pre_device,
-                                                           lp->pre_channel)) < 0) {
-                                        printk(KERN_WARNING
-                                               "isdn_net_start_xmit: No channel for %s\n",
-                                               ndev->name);
+				if ((chi =
+				     isdn_get_free_channel(ISDN_USAGE_NET,
+							   lp->l2_proto,
+							   lp->l3_proto,
+							   lp->pre_device,
+						 lp->pre_channel)) < 0) {
+					printk(KERN_WARNING
+					       "isdn_net_start_xmit: No channel for %s\n",
+					       ndev->name);
 					restore_flags(flags);
-		/* we probably should drop the skb here and return 0 to omit
-		   'socket destroy delayed' messages */
+					/* we probably should drop the skb here and return 0 to omit
+					   'socket destroy delayed' messages */
 					return 1;
 				}
-                                /* Log packet, which triggered dialing */
+				/* Log packet, which triggered dialing */
 				if (dev->net_verbose)
-                                        isdn_net_log_packet(buf, lp);
+					isdn_net_log_packet(buf, lp);
 				lp->dialstate = 1;
 				lp->flags |= ISDN_NET_CONNECTED;
 				/* Connect interface with channel */
@@ -898,59 +975,59 @@
 				if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
 					/* no 'first_skb' handling for syncPPP */
 					if (isdn_ppp_bind(lp) < 0) {
-                                        	dev_kfree_skb(skb,FREE_WRITE);
+						dev_kfree_skb(skb, FREE_WRITE);
 						isdn_net_unbind_channel(lp);
-                                                restore_flags(flags);
+						restore_flags(flags);
 						return 0;	/* STN (skb to nirvana) ;) */
 					}
-	                                isdn_net_dial();	/* Initiate dialing */
 					restore_flags(flags);
+					isdn_net_dial();	/* Initiate dialing */
 					return 1;	/* let upper layer requeue skb packet */
 				}
 #endif
-                                /* remember first skb to speed up arp
-                                 * when using encap ETHER
-                                 */
-                                if (lp->first_skb) {
-                                        printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
-                                        dev_kfree_skb(lp->first_skb,FREE_WRITE);
-                                        lp->first_skb = NULL;
-                                }
-                                lp->first_skb = skb;
+				/* remember first skb to speed up arp
+				 * when using encap ETHER
+				 */
+				if (lp->first_skb) {
+					printk(KERN_WARNING "isdn_net_start_xmit: First skb already set!\n");
+					dev_kfree_skb(lp->first_skb, FREE_WRITE);
+					lp->first_skb = NULL;
+				}
+				lp->first_skb = skb;
 				/* Initiate dialing */
-				isdn_net_dial();
-                                ndev->tbusy = 0;
+				ndev->tbusy = 0;
 				restore_flags(flags);
-                                return 0;
+				isdn_net_dial();
+				return 0;
 			} else {
-                                /*
-                                 * Having no phone-number is a permanent
-                                 * failure or misconfiguration.
-                                 * Instead of just dropping, we should also
-                                 * have the upper layers to respond
-                                 * with an ICMP No route to host in the
-                                 * future, however at the moment, i don't
-                                 * know a simple way to do that.
-                                 * The same applies, when the telecom replies
-                                 * "no destination" to our dialing-attempt.
-                                 */
-                                printk(KERN_WARNING
-                                       "isdn_net: No phone number for %s, packet dropped\n",
-                                       ndev->name);
+				/*
+				 * Having no phone-number is a permanent
+				 * failure or misconfiguration.
+				 * Instead of just dropping, we should also
+				 * have the upper layers to respond
+				 * with an ICMP No route to host in the
+				 * future, however at the moment, i don't
+				 * know a simple way to do that.
+				 * The same applies, when the telecom replies
+				 * "no destination" to our dialing-attempt.
+				 */
+				printk(KERN_WARNING
+				       "isdn_net: No phone number for %s, packet dropped\n",
+				       ndev->name);
 				dev_kfree_skb(skb, FREE_WRITE);
 				ndev->tbusy = 0;
-                                return 0;
+				return 0;
 			}
 		} else {
-                        /* Connection is established, try sending */
+			/* Connection is established, try sending */
 			ndev->trans_start = jiffies;
 			if (!lp->dialstate) {
-                                if (lp->first_skb) {
-                                        if (isdn_net_xmit(ndev,lp,lp->first_skb))
-                                                return 1;
-                                        lp->first_skb = NULL;
-                                }
-				return(isdn_net_xmit(ndev, lp, skb));
+				if (lp->first_skb) {
+					if (isdn_net_xmit(ndev, lp, lp->first_skb))
+						return 1;
+					lp->first_skb = NULL;
+				}
+				return (isdn_net_xmit(ndev, lp, skb));
 			} else
 				ndev->tbusy = 1;
 		}
@@ -986,7 +1063,7 @@
  * Get statistics
  */
 static struct enet_statistics *
- isdn_net_get_stats(struct device *dev)
+isdn_net_get_stats(struct device *dev)
 {
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
 	return &lp->stats;
@@ -1001,51 +1078,50 @@
  *      This is normal practice and works for any 'now in use' protocol.
  */
 
-unsigned short isdn_net_type_trans(struct sk_buff *skb, struct device *dev)
+static unsigned short
+isdn_net_type_trans(struct sk_buff *skb, struct device *dev)
 {
-        struct ethhdr *eth;
-        unsigned char *rawp;
-        
-        skb_pull(skb,ETH_HLEN);
-        eth= skb->mac.ethernet;
-        
-        if(*eth->h_dest&1) {
-                if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
-                        skb->pkt_type=PACKET_BROADCAST;
-                else
-                        skb->pkt_type=PACKET_MULTICAST;
-        }
-        
-        /*
-         *      This ALLMULTI check should be redundant by 1.4
-         *      so don't forget to remove it.
-         */
-        
-        else if (dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) {
-                if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
-                        skb->pkt_type=PACKET_OTHERHOST;
-        }
-
-        if (ntohs(eth->h_proto) >= 1536)
-                return eth->h_proto;
-
-        rawp = skb->data;
-
-        /*
-         *      This is a magic hack to spot IPX packets. Older Novell breaks
-         *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
-         *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
-         *      won't work for fault tolerant netware but does for the rest.
-         */
-        if (*(unsigned short *)rawp == 0xFFFF)
-                return htons(ETH_P_802_3);
-        /*
-         *      Real 802.2 LLC
-         */
-        return htons(ETH_P_802_2);
+	struct ethhdr *eth;
+	unsigned char *rawp;
+
+	skb_pull(skb, ETH_HLEN);
+	eth = skb->mac.ethernet;
+
+	if (*eth->h_dest & 1) {
+		if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+			skb->pkt_type = PACKET_BROADCAST;
+		else
+			skb->pkt_type = PACKET_MULTICAST;
+	}
+	/*
+	 *      This ALLMULTI check should be redundant by 1.4
+	 *      so don't forget to remove it.
+	 */
+
+	else if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+		if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
+			skb->pkt_type = PACKET_OTHERHOST;
+	}
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+
+	rawp = skb->data;
+
+	/*
+	 *      This is a magic hack to spot IPX packets. Older Novell breaks
+	 *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *      won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *) rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+	/*
+	 *      Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
 }
 
-/* 
+/*
  * Got a packet from ISDN-Channel.
  */
 static void
@@ -1053,12 +1129,21 @@
 {
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
 #ifdef CONFIG_ISDN_PPP
-        isdn_net_local *olp = lp;  /* original 'lp' */
+	isdn_net_local *olp = lp;	/* original 'lp' */
+	int proto = PPP_PROTOCOL(skb->data);
 #endif
 
 	lp->transcount += skb->len;
 	lp->stats.rx_packets++;
-	lp->huptimer = 0;
+#ifdef CONFIG_ISDN_PPP
+	/*
+	 * If encapsulation is syncppp, don't reset
+	 * huptimer on LCP packets.
+	 */
+	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
+	    (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
+#endif
+		lp->huptimer = 0;
 
 	if (lp->master) {
 		/* Bundling: If device is a slave-device, deliver to master, also
@@ -1067,49 +1152,56 @@
 		ndev = lp->master;
 		lp = (isdn_net_local *) ndev->priv;
 		lp->stats.rx_packets++;
-		lp->huptimer = 0;
+#ifdef CONFIG_ISDN_PPP
+		/*
+		 * If encapsulation is syncppp, don't reset
+		 * huptimer on LCP packets.
+		 */
+		if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP ||
+		    (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP && proto != PPP_LCP))
+#endif
+			lp->huptimer = 0;
 	}
-
 	skb->dev = ndev;
 	skb->pkt_type = PACKET_HOST;
-        skb->mac.raw = skb->data;
+	skb->mac.raw = skb->data;
 #ifdef ISDN_DEBUG_NET_DUMP
-        isdn_dumppkt("R:", skb->data, skb->len, 40);
+	isdn_dumppkt("R:", skb->data, skb->len, 40);
 #endif
 	switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_ETHER:
-                        /* Ethernet over ISDN */
-                        skb->protocol = isdn_net_type_trans(skb,ndev);
-                        break;
-                case ISDN_NET_ENCAP_UIHDLC:
-                        /* HDLC with UI-frame (for ispa with -h1 option) */
-                        skb_pull(skb,2);
-                        /* Fall through */
-                case ISDN_NET_ENCAP_RAWIP:
-                        /* RAW-IP without MAC-Header */
-                        skb->protocol = htons(ETH_P_IP);
-                        break;
-                case ISDN_NET_ENCAP_CISCOHDLC:
-                        /* CISCO-HDLC IP with type field and  fake I-frame-header */
-                        skb_pull(skb, 2);
-                        /* Fall through */
-                case ISDN_NET_ENCAP_IPTYP:
-                        /* IP with type field */
-                        skb->protocol = *(unsigned short *)&(skb->data[0]);
-                        skb_pull(skb, 2);
-                        if (*(unsigned short *)skb->data == 0xFFFF)
-                                skb->protocol = htons(ETH_P_802_3);
-                        break;
+		case ISDN_NET_ENCAP_ETHER:
+			/* Ethernet over ISDN */
+			skb->protocol = isdn_net_type_trans(skb, ndev);
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-frame (for ispa with -h1 option) */
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_RAWIP:
+			/* RAW-IP without MAC-Header */
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+			/* CISCO-HDLC IP with type field and  fake I-frame-header */
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_IPTYP:
+			/* IP with type field */
+			skb->protocol = *(unsigned short *) &(skb->data[0]);
+			skb_pull(skb, 2);
+			if (*(unsigned short *) skb->data == 0xFFFF)
+				skb->protocol = htons(ETH_P_802_3);
+			break;
 #ifdef CONFIG_ISDN_PPP
-                case ISDN_NET_ENCAP_SYNCPPP:
-                        isdn_ppp_receive(lp->netdev, olp, skb);
-                        return;
-#endif
-                default:
-                        printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
-                               lp->name);
-                        kfree_skb(skb,FREE_READ);
-                        return;
+		case ISDN_NET_ENCAP_SYNCPPP:
+			isdn_ppp_receive(lp->netdev, olp, skb);
+			return;
+#endif
+		default:
+			printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+			       lp->name);
+			kfree_skb(skb, FREE_READ);
+			return;
 	}
 	netif_rx(skb);
 	return;
@@ -1121,34 +1213,7 @@
  * else return 0.
  */
 int
-isdn_net_receive_callback(int idx, u_char * buf, int len)
-{
-	isdn_net_dev *p = dev->rx_netdev[idx];
-	struct sk_buff *skb;
-
-	if (p) {
-		isdn_net_local *lp = &p->local;
-		if ((lp->flags & ISDN_NET_CONNECTED) &&
-		    (!lp->dialstate)) {
-			skb = dev_alloc_skb(len);
-			if (skb == NULL) {
-				printk(KERN_WARNING "out of memory\n");
-				return 0;
-			}
-			memcpy(skb_put(skb, len), buf, len);
-			isdn_net_receive(&p->dev, skb);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-/*
- *  receive callback for lowlevel drivers, which support skb's
- */
-
-int
-isdn_net_rcv_skb(int idx, struct sk_buff *skb) 
+isdn_net_rcv_skb(int idx, struct sk_buff *skb)
 {
 	isdn_net_dev *p = dev->rx_netdev[idx];
 
@@ -1165,43 +1230,41 @@
 
 static int
 my_eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
-              void *daddr, void *saddr, unsigned len)
+	      void *daddr, void *saddr, unsigned len)
 {
-	struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
+	struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
 
-	/* 
+	/*
 	 * Set the protocol type. For a packet of type ETH_P_802_3 we
-         * put the length here instead. It is up to the 802.2 layer to
-         * carry protocol information.
+	 * put the length here instead. It is up to the 802.2 layer to
+	 * carry protocol information.
 	 */
-	
-	if(type!=ETH_P_802_3) 
+
+	if (type != ETH_P_802_3)
 		eth->h_proto = htons(type);
 	else
 		eth->h_proto = htons(len);
 
 	/*
-	 *	Set the source hardware address. 
+	 * Set the source hardware address.
 	 */
-	if(saddr)
-		memcpy(eth->h_source,saddr,dev->addr_len);
+	if (saddr)
+		memcpy(eth->h_source, saddr, dev->addr_len);
 	else
-		memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
 
 	/*
-	 *	Anyway, the loopback-device should never use this function... 
+	 * Anyway, the loopback-device should never use this function...
 	 */
 
 	if (dev->flags & IFF_LOOPBACK) {
 		memset(eth->h_dest, 0, dev->addr_len);
-		return(dev->hard_header_len);
+		return (dev->hard_header_len);
 	}
-	
-	if(daddr) {
-		memcpy(eth->h_dest,daddr,dev->addr_len);
+	if (daddr) {
+		memcpy(eth->h_dest, daddr, dev->addr_len);
 		return dev->hard_header_len;
 	}
-	
 	return -dev->hard_header_len;
 }
 
@@ -1209,76 +1272,107 @@
  *  build an header
  *  depends on encaps that is being used.
  */
- 
+
 static int
 isdn_net_header(struct sk_buff *skb, struct device *dev, unsigned short type,
-                void *daddr, void *saddr, unsigned plen)
+		void *daddr, void *saddr, unsigned plen)
 {
 	isdn_net_local *lp = dev->priv;
 	ushort len = 0;
-	
+
 	switch (lp->p_encap) {
-                case ISDN_NET_ENCAP_ETHER:
-                        len = my_eth_header(skb, dev, type, daddr, saddr, plen);
-                        break;
-                case ISDN_NET_ENCAP_RAWIP:
-                        printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+		case ISDN_NET_ENCAP_ETHER:
+			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
+			break;
+		case ISDN_NET_ENCAP_RAWIP:
+			printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
 			len = 0;
-                        break;
-                case ISDN_NET_ENCAP_IPTYP:
-                        /* ethernet type field */
-                        *((ushort*) skb_push(skb, 2)) = htons(type);
-                        len = 2;
-                        break;
-                case ISDN_NET_ENCAP_UIHDLC:
-                        /* HDLC with UI-Frames (for ispa with -h1 option) */
-                        *((ushort*) skb_push(skb, 2)) = htons(0x0103);
-                        len = 2;
-                        break;
-                case ISDN_NET_ENCAP_CISCOHDLC:
+			break;
+		case ISDN_NET_ENCAP_IPTYP:
+			/* ethernet type field */
+			*((ushort *) skb_push(skb, 2)) = htons(type);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-Frames (for ispa with -h1 option) */
+			*((ushort *) skb_push(skb, 2)) = htons(0x0103);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
 			skb_push(skb, 4);
-                        skb->data[0] = 0x0f;
-                        skb->data[1] = 0x00;
-                        *((ushort*)&skb->data[2]) = htons(type);
-                        len = 4;
-                        break;
+			skb->data[0] = 0x0f;
+			skb->data[1] = 0x00;
+			*((ushort *) & skb->data[2]) = htons(type);
+			len = 4;
+			break;
 	}
 	return len;
 }
 
 /* We don't need to send arp, because we have point-to-point connections. */
-
+#if (LINUX_VERSION_CODE < 0x02010F)
 static int
 isdn_net_rebuild_header(void *buff, struct device *dev, unsigned long dst,
-                        struct sk_buff *skb)
+			struct sk_buff *skb)
 {
 	isdn_net_local *lp = dev->priv;
-        int ret = 0;
+	int ret = 0;
+
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		struct ethhdr *eth = (struct ethhdr *) buff;
+
+		/*
+		 *      Only ARP/IP is currently supported
+		 */
 
-        if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-                struct ethhdr *eth = (struct ethhdr *)buff;
-                
-                /*
-                 *      Only ARP/IP is currently supported
-                 */
-                
-                if(eth->h_proto != htons(ETH_P_IP)) {
-                        printk(KERN_WARNING
-                               "isdn_net: %s don't know how to resolve type %d addresses?\n",
-                               dev->name, (int)eth->h_proto);
-                        memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-                        return 0;
-                }
-                /*
-                 *      Try to get ARP to resolve the header.
-                 */
-#ifdef CONFIG_INET       
-                ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
-#endif  
-        }
+		if (eth->h_proto != htons(ETH_P_IP)) {
+			printk(KERN_WARNING
+			       "isdn_net: %s don't know how to resolve type %d addresses?\n",
+			       dev->name, (int) eth->h_proto);
+			memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+			return 0;
+		}
+		/*
+		 *      Try to get ARP to resolve the header.
+		 */
+#ifdef CONFIG_INET
+		ret = arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb) ? 1 : 0;
+#endif
+	}
 	return ret;
 }
+#else
+static int
+isdn_net_rebuild_header(struct sk_buff *skb)
+{
+	struct device *dev = skb->dev;
+	isdn_net_local *lp = dev->priv;
+	int ret = 0;
+
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		struct ethhdr *eth = (struct ethhdr *) skb->data;
 
+		/*
+		 *      Only ARP/IP is currently supported
+		 */
+
+		if (eth->h_proto != htons(ETH_P_IP)) {
+			printk(KERN_WARNING
+			       "isdn_net: %s don't know how to resolve type %d addresses?\n",
+			       dev->name, (int) eth->h_proto);
+			memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+			return 0;
+		}
+		/*
+		 *      Try to get ARP to resolve the header.
+		 */
+#ifdef CONFIG_INET
+		ret = arp_find(eth->h_dest, skb) ? 1 : 0;
+#endif
+	}
+	return ret;
+}
+#endif
 /*
  * Interface-setup. (called just after registering a new interface)
  */
@@ -1286,8 +1380,9 @@
 isdn_net_init(struct device *ndev)
 {
 	ushort max_hlhdr_len = 0;
-        isdn_net_local *lp = (isdn_net_local *)ndev->priv;
-	int drvidx, i;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int drvidx,
+	 i;
 
 	if (ndev == NULL) {
 		printk(KERN_WARNING "isdn_net_init: dev = NULL!\n");
@@ -1297,54 +1392,61 @@
 		printk(KERN_WARNING "isdn_net_init: dev->priv = NULL!\n");
 		return -ENODEV;
 	}
-
-        ether_setup(ndev);
-        lp->org_hcb               = ndev->header_cache_bind;
-        lp->org_hcu               = ndev->header_cache_update;
+	ether_setup(ndev);
+#if (LINUX_VERSION_CODE < 0x02010F)
+	lp->org_hcb = ndev->header_cache_bind;
+#else
+	lp->org_hhc = ndev->hard_header_cache;
+#endif
+	lp->org_hcu = ndev->header_cache_update;
 
 	/* Setup the generic properties */
 
-        ndev->hard_header         = NULL;
-        ndev->header_cache_bind   = NULL;
-        ndev->header_cache_update = NULL;
-        ndev->mtu                 = 1500;
-        ndev->flags               = IFF_NOARP;
-        ndev->family              = AF_INET;
-        ndev->type                = ARPHRD_ETHER;  
-        ndev->addr_len            = ETH_ALEN;
-        ndev->pa_addr             = 0;
-        ndev->pa_brdaddr          = 0;
-        ndev->pa_mask             = 0;
-        ndev->pa_alen             = 4;
+	ndev->hard_header = NULL;
+#if (LINUX_VERSION_CODE < 0x02010F)
+	ndev->header_cache_bind = NULL;
+#else
+	ndev->hard_header_cache = NULL;
+#endif
+	ndev->header_cache_update = NULL;
+	ndev->mtu = 1500;
+	ndev->flags = IFF_NOARP;
+	ndev->family = AF_INET;
+	ndev->type = ARPHRD_ETHER;
+	ndev->addr_len = ETH_ALEN;
+	ndev->pa_addr = 0;
+	ndev->pa_brdaddr = 0;
+	ndev->pa_mask = 0;
+	ndev->pa_alen = 4;
 
-        for (i = 0; i < ETH_ALEN; i++)
-                ndev->broadcast[i]=0xff;
+	for (i = 0; i < ETH_ALEN; i++)
+		ndev->broadcast[i] = 0xff;
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)
-                skb_queue_head_init(&ndev->buffs[i]);
-	
+		skb_queue_head_init(&ndev->buffs[i]);
+
 	/* The ISDN-specific entries in the device structure. */
-	ndev->open                = &isdn_net_open;
-	ndev->hard_start_xmit     = &isdn_net_start_xmit;
+	ndev->open = &isdn_net_open;
+	ndev->hard_start_xmit = &isdn_net_start_xmit;
 
-	/* 
+	/*
 	 *  up till binding we ask the protocol layer to reserve as much
 	 *  as we might need for HL layer
-         */
-	
+	 */
+
 	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
 		if (dev->drv[drvidx])
 			if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
 				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
 
-	ndev->hard_header_len     = ETH_HLEN + max_hlhdr_len;
+	ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
 
-	ndev->stop                = &isdn_net_close;
-	ndev->get_stats           = &isdn_net_get_stats;
-	ndev->rebuild_header      = &isdn_net_rebuild_header;
+	ndev->stop = &isdn_net_close;
+	ndev->get_stats = &isdn_net_get_stats;
+	ndev->rebuild_header = &isdn_net_rebuild_header;
 
 #ifdef CONFIG_ISDN_PPP
-	ndev->do_ioctl            = isdn_ppp_dev_ioctl;
+	ndev->do_ioctl = isdn_ppp_dev_ioctl;
 #endif
 	return 0;
 }
@@ -1385,35 +1487,35 @@
 
 	for (; *p; s++, p++)
 		switch (*p) {
-                        case '\\':
-                                /*
-                                 * Literal match with following character,
-                                 * fall through.
-                                 */
-                                p++;
-                        default:
-                                if (*s != *p)
-                                        return (0);
-                                continue;
-                        case '?':
-                                /* Match anything. */
-                                if (*s == '\0')
-                                        return (0);
-                                continue;
-                        case '*':
-                                /* Trailing star matches everything. */
-                                return (*++p ? isdn_net_Star(s, p) : 1);
-                        case '[':
-                                /* [^....] means inverse character class. */
-                                if ((reverse = (p[1] == '^')))
-                                        p++;
-                                for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
-                                        /* This next line requires a good C compiler. */
-                                        if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
-                                                matched = 1;
-                                if (matched == reverse)
-                                        return (0);
-                                continue;
+			case '\\':
+				/*
+				 * Literal match with following character,
+				 * fall through.
+				 */
+				p++;
+			default:
+				if (*s != *p)
+					return (0);
+				continue;
+			case '?':
+				/* Match anything. */
+				if (*s == '\0')
+					return (0);
+				continue;
+			case '*':
+				/* Trailing star matches everything. */
+				return (*++p ? isdn_net_Star(s, p) : 1);
+			case '[':
+				/* [^....] means inverse character class. */
+				if ((reverse = (p[1] == '^')))
+					p++;
+				for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+					/* This next line requires a good C compiler. */
+					if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+						matched = 1;
+				if (matched == reverse)
+					return (0);
+				continue;
 		}
 	return (*s == '\0');
 }
@@ -1430,12 +1532,12 @@
 	while (p) {
 		if (p->local.pre_device == drvidx)
 			switch (p->local.pre_channel) {
-			case 0:
-				p->local.pre_channel = 1;
-				break;
-			case 1:
-				p->local.pre_channel = 0;
-				break;
+				case 0:
+					p->local.pre_channel = 1;
+					break;
+				case 1:
+					p->local.pre_channel = 0;
+					break;
 			}
 		p = (isdn_net_dev *) p->next;
 	}
@@ -1472,7 +1574,7 @@
  *               4 = Wait cbdelay, then call back
  */
 int
-isdn_net_find_icall(int di, int ch, int idx, char *num)
+isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
 {
 	char *eaz;
 	int si1;
@@ -1483,40 +1585,24 @@
 	isdn_net_dev *p;
 	isdn_net_phone *n;
 	ulong flags;
-	char nr[31];
-	char *s;
+	char nr[32];
 
 	/* Search name in netdev-chain */
 	save_flags(flags);
 	cli();
-	if (num[0] == ',') {
+	if (!setup.phone[0]) {
 		nr[0] = '0';
-		strncpy(&nr[1], num, 30);
+		nr[1] = '\0';
 		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
 	} else
-		strncpy(nr, num, 30);
-	s = strtok(nr, ",");
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return 0;
-	}
-	si1 = (int)simple_strtoul(s,NULL,10);
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_net: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return 0;
-	}
-	si2 = (int)simple_strtoul(s,NULL,10);
-	eaz = strtok(NULL, ",");
-	if (!eaz) {
+		strcpy(nr, setup.phone);
+	si1 = (int) setup.si1;
+	si2 = (int) setup.si2;
+	if (!setup.eazmsn[0]) {
 		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
 		eaz = "0";
-	}
+	} else
+		eaz = setup.eazmsn;
 	if (dev->net_verbose > 1)
 		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
 	/* Accept only calls with Si1 = 7 (Data-Transmission) */
@@ -1536,12 +1622,12 @@
 	while (p) {
 		/* If last check has triggered as binding-swap, revert it */
 		switch (swapped) {
-		case 2:
-			isdn_net_swap_usage(idx, sidx);
-			/* fall through */
-		case 1:
-			isdn_net_swapbind(di);
-			break;
+			case 2:
+				isdn_net_swap_usage(idx, sidx);
+				/* fall through */
+			case 1:
+				isdn_net_swapbind(di);
+				break;
 		}
 		swapped = 0;
 		if (!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz))
@@ -1550,11 +1636,11 @@
 		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
 		       p->local.name, p->local.msn, p->local.flags, p->local.dialstate);
 #endif
-		if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&	    /* EAZ is matching   */
-		    (((!(p->local.flags & ISDN_NET_CONNECTED)) &&	            /* but not connected */
-		      (USG_NONE(dev->usage[idx]))) ||	                            /* and ch. unused or */
-		     ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) && /* if dialing        */
-		       (!(p->local.flags & ISDN_NET_CALLBACK)))		            /* but no callback   */
+		if ((!strcmp(isdn_map_eaz2msn(p->local.msn, di), eaz)) &&	/* EAZ is matching   */
+		    (((!(p->local.flags & ISDN_NET_CONNECTED)) &&	/* but not connected */
+		      (USG_NONE(dev->usage[idx]))) ||	/* and ch. unused or */
+		     ((((p->local.dialstate == 4) || (p->local.dialstate == 12)) &&	/* if dialing        */
+		       (!(p->local.flags & ISDN_NET_CALLBACK)))	/* but no callback   */
 		     ))) {
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
@@ -1564,13 +1650,13 @@
 				if ((p->local.pre_channel != ch) ||
 				    (p->local.pre_device != di)) {
 					/* Here we got a problem:
-					   If using an ICN-Card, an incoming call is always signaled on
-					   on the first channel of the card, if both channels are
-					   down. However this channel may be bound exclusive. If the
-					   second channel is free, this call should be accepted.
-					   The solution is horribly but it runs, so what:
-					   We exchange the exclusive bindings of the two channels, the
-					   corresponding variables in the interface-structs.
+					 * If using an ICN-Card, an incoming call is always signaled on
+					 * on the first channel of the card, if both channels are
+					 * down. However this channel may be bound exclusive. If the
+					 * second channel is free, this call should be accepted.
+					 * The solution is horribly but it runs, so what:
+					 * We exchange the exclusive bindings of the two channels, the
+					 * corresponding variables in the interface-structs.
 					 */
 					if (ch == 0) {
 						sidx = isdn_dc2minor(di, 1);
@@ -1579,13 +1665,13 @@
 #endif
 						if (USG_NONE(dev->usage[sidx])) {
 							/* Second Channel is free, now see if it is bound
-							   exclusive too. */
+							 * exclusive too. */
 							if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
 #ifdef ISDN_DEBUG_NET_ICALL
 								printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
 #endif
 								/* Yes, swap bindings only, if the original
-								   binding is bound to channel 1 of this driver */
+								 * binding is bound to channel 1 of this driver */
 								if ((p->local.pre_device == di) &&
 								    (p->local.pre_channel == 1)) {
 									isdn_net_swapbind(di);
@@ -1627,7 +1713,7 @@
 						continue;
 					}
 				}
-			} /* if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) */
+			}
 #ifdef ISDN_DEBUG_NET_ICALL
 			printk(KERN_DEBUG "n_fi: match2\n");
 #endif
@@ -1692,7 +1778,7 @@
 							return 0;
 						}
 						/* Setup dialstate. */
-                                                lp->dtimer = 0;
+						lp->dtimer = 0;
 						lp->dialstate = 11;
 						lp->flags |= ISDN_NET_CONNECTED;
 						/* Connect interface with channel */
@@ -1707,7 +1793,7 @@
 #endif
 						/* Initiate dialing by returning 2 or 4 */
 						restore_flags(flags);
-						return (lp->flags & ISDN_NET_CBHUP)?2:4;
+						return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
 					} else
 						printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
 					restore_flags(flags);
@@ -1717,25 +1803,29 @@
 					       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 == 4) || (p->local.dialstate == 12)) {
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							isdn_ppp_free(lp);
+#endif
 						isdn_free_channel(p->local.isdn_device, p->local.isdn_channel,
 							 ISDN_USAGE_NET);
+					}
 					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
 					dev->usage[idx] |= ISDN_USAGE_NET;
 					strcpy(dev->num[idx], nr);
 					isdn_info_update();
-                                        dev->st_netdev[idx] = lp->netdev;
+					dev->st_netdev[idx] = lp->netdev;
 					p->local.isdn_device = di;
 					p->local.isdn_channel = ch;
 					p->local.ppp_slot = -1;
-					p->local.pppbind = -1;
 					p->local.flags |= ISDN_NET_CONNECTED;
 					p->local.dialstate = 7;
 					p->local.dtimer = 0;
 					p->local.outgoing = 0;
 					p->local.huptimer = 0;
-					p->local.hupflags |= 1;
-                                        p->local.hupflags &= ~2;
+					p->local.hupflags |= ISDN_WAITCHARGE;
+					p->local.hupflags &= ~ISDN_HAVECHARGE;
 #ifdef CONFIG_ISDN_PPP
 					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
 						if (isdn_ppp_bind(lp) < 0) {
@@ -1762,7 +1852,7 @@
  * Search list of net-interfaces for an interface with given name.
  */
 isdn_net_dev *
- isdn_net_findif(char *name)
+isdn_net_findif(char *name)
 {
 	isdn_net_dev *p = dev->netdev;
 
@@ -1779,7 +1869,8 @@
  * This is called from the userlevel-routine below or
  * from isdn_net_start_xmit().
  */
-int isdn_net_force_dial_lp(isdn_net_local * lp)
+int
+isdn_net_force_dial_lp(isdn_net_local * lp)
 {
 	if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
 		int chi;
@@ -1789,8 +1880,8 @@
 			cli();
 			/* Grab a free ISDN-Channel */
 			if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
-						    lp->l3_proto,
-						    lp->pre_device,
+							 lp->l3_proto,
+							 lp->pre_device,
 						 lp->pre_channel)) < 0) {
 				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
 				restore_flags(flags);
@@ -1809,8 +1900,8 @@
 				}
 #endif
 			/* Initiate dialing */
-			isdn_net_dial();
 			restore_flags(flags);
+			isdn_net_dial();
 			return 0;
 		} else
 			return -EINVAL;
@@ -1822,7 +1913,7 @@
  * Force a net-interface to dial out.
  * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
  */
-int 
+int
 isdn_net_force_dial(char *name)
 {
 	isdn_net_dev *p = isdn_net_findif(name);
@@ -1854,9 +1945,9 @@
 		strcpy(netdev->local.name, "         ");
 	else
 		strcpy(netdev->local.name, name);
-	netdev->dev.name      = netdev->local.name;
-	netdev->dev.priv      = &netdev->local;
-	netdev->dev.init      = isdn_net_init;
+	netdev->dev.name = netdev->local.name;
+	netdev->dev.priv = &netdev->local;
+	netdev->dev.init = isdn_net_init;
 	netdev->local.p_encap = ISDN_NET_ENCAP_RAWIP;
 	if (master) {
 		/* Device shall be a slave */
@@ -1884,7 +1975,7 @@
 	netdev->local.magic = ISDN_NET_MAGIC;
 
 #ifdef CONFIG_ISDN_PPP
-	netdev->mp_last = NULL;	/* mpqueue is empty */
+	netdev->mp_last = NULL; /* mpqueue is empty */
 	netdev->ib.next_num = 0;
 	netdev->ib.last = NULL;
 #endif
@@ -1900,16 +1991,18 @@
 	netdev->local.exclusive = -1;
 	netdev->local.ppp_slot = -1;
 	netdev->local.pppbind = -1;
+	netdev->local.sav_skb = NULL;
+	netdev->local.first_skb = NULL;
 	netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
 	netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
 	netdev->local.slavedelay = 10 * HZ;
 	netdev->local.srobin = &netdev->dev;
-	netdev->local.hupflags = 8;	/* Do hangup even on incoming calls */
+	netdev->local.hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
 	netdev->local.onhtime = 10;	/* Default hangup-time for saving costs
-					   of those who forget configuring this */
+	   of those who forget configuring this */
 	netdev->local.dialmax = 1;
-        netdev->local.flags = ISDN_NET_CBHUP; /* Hangup before Callback */
-        netdev->local.cbdelay = 25;      /* Wait 5 secs before Callback */
+	netdev->local.flags = ISDN_NET_CBHUP;	/* Hangup before Callback */
+	netdev->local.cbdelay = 25;	/* Wait 5 secs before Callback */
 	/* Put into to netdev-chain */
 	netdev->next = (void *) dev->netdev;
 	dev->netdev = netdev;
@@ -1935,6 +2028,9 @@
 		/* Master must be a real interface, not a slave */
 		if (n->local.master)
 			return NULL;
+		/* Master must not be started yet */
+		if (n->dev.start)
+			return NULL;
 		return (isdn_net_new(newname, &(n->dev)));
 	}
 	return NULL;
@@ -1946,7 +2042,8 @@
  * for not overwriting existing setups. It has to get the current
  * setup first, if only selected parameters are to be changed.
  */
-int isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+int
+isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
 {
 	isdn_net_dev *p = isdn_net_findif(cfg->name);
 	ulong features;
@@ -1966,30 +2063,34 @@
 			printk(KERN_WARNING "isdn_net: No driver with selected features\n");
 			return -ENODEV;
 		}
-                if (p->local.p_encap != cfg->p_encap)
-                        if (p->dev.start) {
-                                printk(KERN_WARNING
-                                       "%s: cannot change encap when if is up\n",
-                                       p->local.name);
-                                return -EBUSY;
-                        }
+		if (p->local.p_encap != cfg->p_encap)
+			if (p->dev.start) {
+				printk(KERN_WARNING
+				"%s: cannot change encap when if is up\n",
+				       p->local.name);
+				return -EBUSY;
+			}
+		if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
 #ifndef CONFIG_ISDN_PPP
-                if (cfg->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
-                        printk(KERN_WARNING "%s: SyncPPP not configured\n",
-                               p->local.name);
-                        return -EINVAL;
-                }
+			printk(KERN_WARNING "%s: SyncPPP support not configured\n",
+			       p->local.name);
+			return -EINVAL;
+#else
+			p->dev.type = ARPHRD_PPP;	/* change ARP type */
+			p->dev.addr_len = 0;
 #endif
+		}
 		if (strlen(cfg->drvid)) {
 			/* A bind has been requested ... */
-			char *c,*e;
+			char *c,
+			*e;
 
 			drvidx = -1;
 			chidx = -1;
 			strcpy(drvid, cfg->drvid);
 			if ((c = strchr(drvid, ','))) {
 				/* The channel-number is appended to the driver-Id with a comma */
-				chidx = (int)simple_strtoul(c + 1,&e,10);
+				chidx = (int) simple_strtoul(c + 1, &e, 10);
 				if (e == c)
 					chidx = -1;
 				*c = '\0';
@@ -2014,9 +2115,9 @@
 			/* If binding is exclusive, try to grab the channel */
 			save_flags(flags);
 			if ((i = isdn_get_free_channel(ISDN_USAGE_NET, p->local.l2_proto,
-						  p->local.l3_proto,
-						  drvidx,
-						  chidx)) < 0) {
+						       p->local.l3_proto,
+						       drvidx,
+						       chidx)) < 0) {
 				/* Grab failed, because desired channel is in use */
 				p->local.exclusive = -1;
 				restore_flags(flags);
@@ -2032,22 +2133,22 @@
 			p->local.exclusive = -1;
 			if ((p->local.pre_device != -1) && (cfg->exclusive == -1)) {
 				isdn_unexclusive_channel(p->local.pre_device, p->local.pre_channel);
-                                isdn_free_channel(p->local.pre_device, p->local.pre_channel,ISDN_USAGE_NET);
+				isdn_free_channel(p->local.pre_device, p->local.pre_channel, ISDN_USAGE_NET);
 				drvidx = -1;
 				chidx = -1;
 			}
 		}
 		strcpy(p->local.msn, cfg->eaz);
-		p->local.pre_device  = drvidx;
+		p->local.pre_device = drvidx;
 		p->local.pre_channel = chidx;
-		p->local.onhtime     = cfg->onhtime;
-		p->local.charge      = cfg->charge;
-		p->local.l2_proto    = cfg->l2_proto;
-		p->local.l3_proto    = cfg->l3_proto;
-                p->local.cbdelay     = cfg->cbdelay;
-                p->local.dialmax     = cfg->dialmax;
-		p->local.slavedelay  = cfg->slavedelay * HZ;
-		p->local.pppbind     = cfg->pppbind;
+		p->local.onhtime = cfg->onhtime;
+		p->local.charge = cfg->charge;
+		p->local.l2_proto = cfg->l2_proto;
+		p->local.l3_proto = cfg->l3_proto;
+		p->local.cbdelay = cfg->cbdelay;
+		p->local.dialmax = cfg->dialmax;
+		p->local.slavedelay = cfg->slavedelay * HZ;
+		p->local.pppbind = cfg->pppbind;
 		if (cfg->secure)
 			p->local.flags |= ISDN_NET_SECURE;
 		else
@@ -2057,47 +2158,63 @@
 		else
 			p->local.flags &= ~ISDN_NET_CBHUP;
 		switch (cfg->callback) {
-                        case 0:
-                                p->local.flags &= ~(ISDN_NET_CALLBACK|ISDN_NET_CBOUT);
-                                break;
-                        case 1:
-                                p->local.flags |= ISDN_NET_CALLBACK;
-                                p->local.flags &= ~ISDN_NET_CBOUT;
-                                break;
-                        case 2:
-                                p->local.flags |= ISDN_NET_CBOUT;
-                                p->local.flags &= ~ISDN_NET_CALLBACK;
-                                break;
-                }
+			case 0:
+				p->local.flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
+				break;
+			case 1:
+				p->local.flags |= ISDN_NET_CALLBACK;
+				p->local.flags &= ~ISDN_NET_CBOUT;
+				break;
+			case 2:
+				p->local.flags |= ISDN_NET_CBOUT;
+				p->local.flags &= ~ISDN_NET_CALLBACK;
+				break;
+		}
 		if (cfg->chargehup)
-			p->local.hupflags |= 4;
+			p->local.hupflags |= ISDN_CHARGEHUP;
 		else
-			p->local.hupflags &= ~4;
+			p->local.hupflags &= ~ISDN_CHARGEHUP;
 		if (cfg->ihup)
-			p->local.hupflags |= 8;
+			p->local.hupflags |= ISDN_INHUP;
 		else
-			p->local.hupflags &= ~8;
+			p->local.hupflags &= ~ISDN_INHUP;
+		if (cfg->chargeint > 10) {
+			p->local.hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE;
+			p->local.chargeint = cfg->chargeint * HZ;
+		}
 		if (cfg->p_encap != p->local.p_encap) {
-                        if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
-                                p->dev.hard_header         = NULL;
-                                p->dev.header_cache_bind   = NULL;
-                                p->dev.header_cache_update = NULL;
-                                p->dev.flags               = IFF_NOARP;
-                        } else {
-                                p->dev.hard_header = isdn_net_header;
-                                if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
-                                        p->dev.header_cache_bind   = p->local.org_hcb;
-                                        p->dev.header_cache_update = p->local.org_hcu;
-                                        p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
-                                } else {
-                                        p->dev.header_cache_bind   = NULL;
-                                        p->dev.header_cache_update = NULL;
-                                        p->dev.flags               = IFF_NOARP;
-                                }
-                        }
-                }
-		p->local.p_encap     = cfg->p_encap;
-                return 0;
+			if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+				p->dev.hard_header = NULL;
+#if (LINUX_VERSION_CODE < 0x02010F)
+				p->dev.header_cache_bind = NULL;
+#else
+				p->dev.hard_header_cache = NULL;
+#endif
+				p->dev.header_cache_update = NULL;
+				p->dev.flags = IFF_NOARP;
+			} else {
+				p->dev.hard_header = isdn_net_header;
+				if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
+#if (LINUX_VERSION_CODE < 0x02010F)
+					p->dev.header_cache_bind = p->local.org_hcb;
+#else
+					p->dev.hard_header_cache = p->local.org_hhc;
+#endif
+					p->dev.header_cache_update = p->local.org_hcu;
+					p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+				} else {
+#if (LINUX_VERSION_CODE < 0x02010F)
+					p->dev.header_cache_bind = NULL;
+#else
+					p->dev.hard_header_cache = NULL;
+#endif
+					p->dev.header_cache_update = NULL;
+					p->dev.flags = IFF_NOARP;
+				}
+			}
+		}
+		p->local.p_encap = cfg->p_encap;
+		return 0;
 	}
 	return -ENODEV;
 }
@@ -2105,7 +2222,8 @@
 /*
  * Perform get-interface-parameters.ioctl
  */
-int isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+int
+isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
 {
 	isdn_net_dev *p = isdn_net_findif(cfg->name);
 
@@ -2124,16 +2242,18 @@
 		cfg->p_encap = p->local.p_encap;
 		cfg->secure = (p->local.flags & ISDN_NET_SECURE) ? 1 : 0;
 		cfg->callback = 0;
-                if (p->local.flags & ISDN_NET_CALLBACK)
-                        cfg->callback = 1;
-                if (p->local.flags & ISDN_NET_CBOUT)
-                        cfg->callback = 2;
+		if (p->local.flags & ISDN_NET_CALLBACK)
+			cfg->callback = 1;
+		if (p->local.flags & ISDN_NET_CBOUT)
+			cfg->callback = 2;
 		cfg->cbhup = (p->local.flags & ISDN_NET_CBHUP) ? 1 : 0;
 		cfg->chargehup = (p->local.hupflags & 4) ? 1 : 0;
 		cfg->ihup = (p->local.hupflags & 8) ? 1 : 0;
-                cfg->cbdelay = p->local.cbdelay;
-                cfg->dialmax = p->local.dialmax;
+		cfg->cbdelay = p->local.cbdelay;
+		cfg->dialmax = p->local.dialmax;
 		cfg->slavedelay = p->local.slavedelay / HZ;
+		cfg->chargeint = (p->local.hupflags & ISDN_CHARGEHUP) ?
+		    (p->local.chargeint / HZ) : 0;
 		cfg->pppbind = p->local.pppbind;
 		if (p->local.slave)
 			strcpy(cfg->slave, ((isdn_net_local *) p->local.slave->priv)->name);
@@ -2151,7 +2271,8 @@
 /*
  * Add a phone-number to an interface.
  */
-int isdn_net_addphone(isdn_net_ioctl_phone * phone)
+int
+isdn_net_addphone(isdn_net_ioctl_phone * phone)
 {
 	isdn_net_dev *p = isdn_net_findif(phone->name);
 	isdn_net_phone *n;
@@ -2172,7 +2293,8 @@
 /*
  * Return a string of all phone-numbers of an interface.
  */
-int isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
+int
+isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
 {
 	isdn_net_dev *p = isdn_net_findif(phone->name);
 	int inout = phone->outgoing & 1;
@@ -2180,38 +2302,39 @@
 	int count = 0;
 	isdn_net_phone *n;
 	int flags;
-	int ret = 0;
+	int ret;
 
 	if (!p)
 		return -ENODEV;
 	save_flags(flags);
 	cli();
 	inout &= 1;
-        for (n = p->local.phone[inout]; n; n = n->next) {
+	for (n = p->local.phone[inout]; n; n = n->next) {
 		if (more) {
-			ret = put_user(((char)' '), phones);
-			phones++; 
+			put_user(' ', phones++);
 			count++;
 		}
-		if (ret || copy_to_user(phones, n->num, strlen(n->num) + 1)) {
+		if ((ret = verify_area(VERIFY_WRITE, (void *) phones, strlen(n->num) + 1))) {
 			restore_flags(flags);
-			return -EFAULT;
+			return ret;
 		}
+		copy_to_user(phones, n->num, strlen(n->num) + 1);
 		phones += strlen(n->num);
 		count += strlen(n->num);
 		more = 1;
 	}
-    ret = put_user(((char)0),phones);  
-    count++;
+	put_user(0, phones);
+	count++;
 	restore_flags(flags);
-	return ret ? -EFAULT : count;
+	return count;
 }
 
 /*
  * Delete a phone-number from an interface.
  */
 
-int isdn_net_delphone(isdn_net_ioctl_phone * phone)
+int
+isdn_net_delphone(isdn_net_ioctl_phone * phone)
 {
 	isdn_net_dev *p = isdn_net_findif(phone->name);
 	int inout = phone->outgoing & 1;
@@ -2241,7 +2364,8 @@
 /*
  * Delete all phone-numbers of an interface.
  */
-static int isdn_net_rmallphone(isdn_net_dev * p)
+static int
+isdn_net_rmallphone(isdn_net_dev * p)
 {
 	isdn_net_phone *n;
 	isdn_net_phone *m;
@@ -2266,19 +2390,15 @@
 /*
  * Force a hangup of a network-interface.
  */
-int isdn_net_force_hangup(char *name)
+int
+isdn_net_force_hangup(char *name)
 {
 	isdn_net_dev *p = isdn_net_findif(name);
-	int flags;
 	struct device *q;
 
 	if (p) {
-		save_flags(flags);
-		cli();
-		if (p->local.isdn_device < 0) {
-			restore_flags(flags);
+		if (p->local.isdn_device < 0)
 			return 1;
-		}
 		isdn_net_hangup(&p->dev);
 		q = p->local.slave;
 		/* If this interface has slaves, do a hangup for them also. */
@@ -2286,7 +2406,6 @@
 			isdn_net_hangup(q);
 			q = (((isdn_net_local *) q->priv)->slave);
 		}
-		restore_flags(flags);
 		return 0;
 	}
 	return -ENODEV;
@@ -2295,7 +2414,8 @@
 /*
  * Helper-function for isdn_net_rm: Do the real work.
  */
-static int isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+static int
+isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
 {
 	int flags;
 
@@ -2348,9 +2468,6 @@
 		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
 	restore_flags(flags);
 
-#ifdef CONFIG_ISDN_PPP
-	isdn_ppp_free_mpqueue(p);	/* still necessary? */
-#endif
 	kfree(p);
 
 	return 0;
@@ -2359,7 +2476,8 @@
 /*
  * Remove a single network-interface.
  */
-int isdn_net_rm(char *name)
+int
+isdn_net_rm(char *name)
 {
 	isdn_net_dev *p;
 	isdn_net_dev *q;
@@ -2382,7 +2500,8 @@
 /*
  * Remove all network-interfaces
  */
-int isdn_net_rmall(void)
+int
+isdn_net_rmall(void)
 {
 	int flags;
 	int ret;
@@ -2395,7 +2514,7 @@
 			/* Remove master-devices only, slaves get removed with their master */
 			if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
 				restore_flags(flags);
-			        return ret;
+				return ret;
 			}
 		}
 	}
@@ -2404,28 +2523,18 @@
 	return 0;
 }
 
-/* 
+/*
  * helper function to flush device queues
  * the better place would be net/core/dev.c
  */
-void dev_purge_queues(struct device *dev)
+static void
+dev_purge_queues(struct device *dev)
 {
 	int i;
-	for(i=0;i<DEV_NUMBUFFS;i++) {
+	for (i = 0; i < DEV_NUMBUFFS; i++) {
 		struct sk_buff *skb;
-		while((skb=skb_dequeue(&dev->buffs[i])))
-				dev_kfree_skb(skb,FREE_WRITE);
-        }
-	
-}
-
-
-
-
-
-
-
-
-
-
+		while ((skb = skb_dequeue(&dev->buffs[i])))
+			dev_kfree_skb(skb, FREE_WRITE);
+	}
 
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_net.h linux/drivers/isdn/isdn_net.h
--- v2.1.26/linux/drivers/isdn/isdn_net.h	Sun Apr 21 01:56:14 1996
+++ linux/drivers/isdn/isdn_net.h	Tue Feb 25 17:12:50 1997
@@ -1,11 +1,11 @@
-/* $Id: isdn_net.h,v 1.2 1996/04/20 16:29:43 fritz Exp $
- *
+/* $Id: isdn_net.h,v 1.5 1997/02/10 20:12:47 fritz Exp $
+
  * header for Linux ISDN subsystem, network related functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
  * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -18,9 +18,18 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_net.h,v $
+ * Revision 1.5  1997/02/10 20:12:47  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.4  1997/02/03 23:16:48  fritz
+ * Removed isdn_net_receive_callback prototype.
+ *
+ * Revision 1.3  1997/01/17 01:19:30  fritz
+ * Applied chargeint patch.
+ *
  * Revision 1.2  1996/04/20 16:29:43  fritz
  * Misc. typos
  *
@@ -29,24 +38,30 @@
  *
  */
 
-extern char*         isdn_net_new(char *, struct device *);
-extern char*         isdn_net_newslave(char *);
-extern int           isdn_net_rm(char *);
-extern int           isdn_net_rmall(void);
-extern int           isdn_net_stat_callback(int, int);
-extern int           isdn_net_receive_callback(int, u_char *, int);
-extern int           isdn_net_setcfg(isdn_net_ioctl_cfg *);
-extern int           isdn_net_getcfg(isdn_net_ioctl_cfg *);
-extern int           isdn_net_addphone(isdn_net_ioctl_phone *);
-extern int           isdn_net_getphones(isdn_net_ioctl_phone *, char *);
-extern int           isdn_net_delphone(isdn_net_ioctl_phone *);
-extern int           isdn_net_find_icall(int, int, int, char *);
-extern void          isdn_net_hangup(struct device *);
-extern void          isdn_net_dial(void);
-extern void          isdn_net_autohup(void);
-extern int           isdn_net_force_hangup(char *);
-extern int           isdn_net_force_dial(char *);
-extern isdn_net_dev* isdn_net_findif(char *);
-extern int           isdn_net_send_skb(struct device *, isdn_net_local *, 
-				      struct sk_buff *);
-extern int           isdn_net_rcv_skb(int, struct sk_buff *); 
+			      /* Definitions for hupflags:                */
+#define ISDN_WAITCHARGE  1      /* did not get a charge info yet            */
+#define ISDN_HAVECHARGE  2      /* We know a charge info                    */
+#define ISDN_CHARGEHUP   4      /* We want to use the charge mechanism      */
+#define ISDN_INHUP       8      /* Even if incoming, close after huptimeout */
+#define ISDN_MANCHARGE  16      /* Charge Interval manually set             */
+
+extern char *isdn_net_new(char *, struct device *);
+extern char *isdn_net_newslave(char *);
+extern int isdn_net_rm(char *);
+extern int isdn_net_rmall(void);
+extern int isdn_net_stat_callback(int, int);
+extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
+extern int isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int isdn_net_find_icall(int, int, int, setup_parm);
+extern void isdn_net_hangup(struct device *);
+extern void isdn_net_dial(void);
+extern void isdn_net_autohup(void);
+extern int isdn_net_force_hangup(char *);
+extern int isdn_net_force_dial(char *);
+extern isdn_net_dev *isdn_net_findif(char *);
+extern int isdn_net_send_skb(struct device *, isdn_net_local *,
+			     struct sk_buff *);
+extern int isdn_net_rcv_skb(int, struct sk_buff *);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v2.1.26/linux/drivers/isdn/isdn_ppp.c	Wed Oct 30 04:26:48 1996
+++ linux/drivers/isdn/isdn_ppp.c	Tue Feb 25 17:12:50 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_ppp.c,v 1.14 1996/08/12 16:26:47 hipp Exp $
+/* $Id: isdn_ppp.c,v 1.25 1997/02/12 20:37:35 hipp Exp $
  *
  * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
  * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -16,9 +16,45 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.c,v $
+ * Revision 1.25  1997/02/12 20:37:35  hipp
+ * New ioctl() PPPIOCGCALLINFO, minor cleanup
+ *
+ * Revision 1.24  1997/02/11 18:32:56  fritz
+ * Bugfix in isdn_ppp_free_mpqueue().
+ *
+ * Revision 1.23  1997/02/10 11:12:19  fritz
+ * More changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.22  1997/02/06 15:03:51  hipp
+ * changed GFP_KERNEL kmalloc to GFP_ATOMIC in isdn_ppp_fill_mpqueue()
+ *
+ * Revision 1.21  1997/02/03 23:29:38  fritz
+ * Reformatted according CodingStyle
+ * Bugfix: removed isdn_ppp_skb_destructor, used by upper layers.
+ * Misc changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.20  1996/10/30 12:21:58  fritz
+ * Cosmetic fix: Compiler warning when compiling without MPP.
+ *
+ * Revision 1.19  1996/10/25 19:03:21  hipp
+ * changed/added some defines to (re)allow compilation without MP/VJ
+ *
+ * Revision 1.18  1996/10/22 23:14:00  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
+ * Revision 1.17  1996/10/22 09:39:49  hipp
+ * a few MP changes and bugfixes
+ *
+ * Revision 1.16  1996/09/23 01:58:10  fritz
+ * Fix: With syncPPP encapsulation, discard LCP packets
+ *      when calculating hangup timeout.
+ *
+ * Revision 1.15  1996/09/07 12:50:12  hipp
+ * bugfixes (unknown device after failed dial attempt, minor bugs)
+ *
  * Revision 1.14  1996/08/12 16:26:47  hipp
  * code cleanup
  * changed connection management from minors to slots
@@ -73,46 +109,84 @@
 
 /* TODO: right tbusy handling when using MP */
 
-#include <asm/uaccess.h>
+/*
+ * experimental for dynamic addressing: readdress IP frames
+ */
+#undef ISDN_SYNCPPP_READDRESS
+
 #include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
+#include <linux/version.h>
 #include <linux/isdn.h>
+#if (LINUX_VERSION_CODE >= 0x020117)
+#include <asm/poll.h>
+#endif
 #include "isdn_common.h"
 #include "isdn_ppp.h"
 #include "isdn_net.h"
 
 #ifndef PPP_IPX
-#define PPP_IPX 0x002b 
+#define PPP_IPX 0x002b
 #endif
- 
+
+/* set this if you use dynamic addressing */
+
 /* Prototypes */
-static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot);
+static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
 static int isdn_ppp_closewait(int slot);
 static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
-			struct sk_buff *skb, int proto);
+				 struct sk_buff *skb, int proto);
 static int isdn_ppp_if_get_unit(char *namebuf);
 
 #ifdef CONFIG_ISDN_MPP
 static int isdn_ppp_bundle(struct ippp_struct *, int unit);
 static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask);
-static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min);
+static void isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min);
+static void isdn_ppp_cleanup_sqqueue(isdn_net_dev * dev, isdn_net_local *, long min);
+static void isdn_ppp_free_sqqueue(isdn_net_dev *);
 static int isdn_ppp_fill_mpqueue(isdn_net_dev *, struct sk_buff **skb,
-		int BEbyte, int *sqno, int min_sqno);
+				 int BEbyte, long *sqno, int min_sqno);
+static void isdn_ppp_free_mpqueue(isdn_net_dev *);
 #endif
 
-char *isdn_ppp_revision              = "$Revision: 1.14 $";
-struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+char *isdn_ppp_revision = "$Revision: 1.25 $";
+
+static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
 
 extern int isdn_net_force_dial_lp(isdn_net_local *);
 
 /*
- * unbind isdn_net_local <=> ippp-device 
+ * frame log (debug)
+ */
+static void
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen)
+{
+	int cnt,
+	 j,
+	 i;
+	char buf[80];
+
+	if (len < maxlen)
+		maxlen = len;
+
+	for (i = 0, cnt = 0; cnt < maxlen; i++) {
+		for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
+			sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
+		printk(KERN_DEBUG "%s[%d]: %s\n", info, i, buf);
+	}
+}
+
+/*
+ * unbind isdn_net_local <=> ippp-device
  * note: it can happen, that we hangup/free the master before the slaves
  */
-int isdn_ppp_free(isdn_net_local *lp)
+int
+isdn_ppp_free(isdn_net_local * lp)
 {
-	isdn_net_local *master_lp=lp;
+#ifdef CONFIG_ISDN_MPP
+	isdn_net_local *master_lp = lp;
+#endif
 	unsigned long flags;
 	struct ippp_struct *is;
 
@@ -124,15 +198,15 @@
 	save_flags(flags);
 	cli();
 #ifdef CONFIG_ISDN_MPP
-	if(lp->master)
+	if (lp->master)
 		master_lp = (isdn_net_local *) lp->master->priv;
 
 	lp->last->next = lp->next;
 	lp->next->last = lp->last;
-	if(master_lp->netdev->queue == lp) {
+	if (master_lp->netdev->queue == lp) {
 		master_lp->netdev->queue = lp->next;
-		if(lp->next == lp) {	/* last link in queue? */
-               		master_lp->netdev->ib.bundled = 0;
+		if (lp->next == lp) {	/* last link in queue? */
+			master_lp->netdev->ib.bundled = 0;
 			isdn_ppp_free_mpqueue(master_lp->netdev);
 			isdn_ppp_free_sqqueue(master_lp->netdev);
 		}
@@ -140,13 +214,21 @@
 	lp->next = lp->last = lp;	/* (re)set own pointers */
 #endif
 
-	isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
+	if ((is->state & IPPP_CONNECT))
+		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
+	else if (is->state & IPPP_ASSIGNED)
+		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGEND' staet */
+
 
-	if(is->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp,(long) is->lp);
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
 
-	is->lp = NULL;	/* link is down .. set lp to NULL */
-	lp->ppp_slot = -1;			/* is this OK ?? */
+	is->lp = NULL;          /* link is down .. set lp to NULL */
+#ifdef ISDN_SYNCPPP_READDRESS
+	is->old_pa_addr = 0x0;
+	is->old_pa_dstaddr = 0x0;
+#endif
+	lp->ppp_slot = -1;      /* is this OK ?? */
 	restore_flags(flags);
 
 	return 0;
@@ -155,7 +237,8 @@
 /*
  * bind isdn_net_local <=> ippp-device
  */
-int isdn_ppp_bind(isdn_net_local * lp)
+int
+isdn_ppp_bind(isdn_net_local * lp)
 {
 	int i;
 	int unit = 0;
@@ -168,14 +251,13 @@
 	save_flags(flags);
 	cli();
 
-	if(lp->pppbind < 0)	/* device bounded to ippp device ? */
-	{
- 		isdn_net_dev *net_dev = dev->netdev;
+	if (lp->pppbind < 0) {  /* device bounded to ippp device ? */
+		isdn_net_dev *net_dev = dev->netdev;
 		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
-		memset(exclusive,0,ISDN_MAX_CHANNELS);
+		memset(exclusive, 0, ISDN_MAX_CHANNELS);
 		while (net_dev) {	/* step through net devices to find exclusive minors */
 			isdn_net_local *lp = &net_dev->local;
-			if(lp->pppbind >= 0)
+			if (lp->pppbind >= 0)
 				exclusive[lp->pppbind] = 1;
 			net_dev = net_dev->next;
 		}
@@ -183,14 +265,13 @@
 		 * search a free device / slot
 		 */
 		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */
+			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) {	/* OPEN, but not connected! */
 				break;
 			}
 		}
-	}
-	else {
-		for(i=0;i<ISDN_MAX_CHANNELS;i++)
-			if(ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+	} else {
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+			if (ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
 				break;
 	}
 
@@ -199,18 +280,16 @@
 		printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
 		return -1;
 	}
-
-	unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */
-	if(unit < 0) {
-		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",lp->name);
+	unit = isdn_ppp_if_get_unit(lp->name);	/* get unit number from interface name .. ugly! */
+	if (unit < 0) {
+		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name);
 		return -1;
 	}
-
 	lp->ppp_slot = i;
 	is = ippp_table[i];
 	is->lp = lp;
 	is->unit = unit;
-	is->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+	is->state = IPPP_OPEN | IPPP_ASSIGNED;	/* assigned to a netdevice but not connected */
 
 	restore_flags(flags);
 
@@ -222,18 +301,25 @@
  * (wakes up daemon after B-channel connect)
  */
 
-void isdn_ppp_wakeup_daemon(isdn_net_local *lp)
+void
+isdn_ppp_wakeup_daemon(isdn_net_local * lp)
 {
+	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS)
+		return;
+
+	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+
 	if (ippp_table[lp->ppp_slot]->wq)
 		wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
 }
 
 /*
  * there was a hangup on the netdevice
- * force wakeup of the ippp device 
+ * force wakeup of the ippp device
  * go into 'device waits for release' state
  */
-static int isdn_ppp_closewait(int slot)
+static int
+isdn_ppp_closewait(int slot)
 {
 	struct ippp_struct *is;
 
@@ -252,53 +338,58 @@
  * isdn_ppp_find_slot / isdn_ppp_free_slot
  */
 
-static int isdn_ppp_get_slot(void)
+static int
+isdn_ppp_get_slot(void)
 {
 	int i;
-	for(i=0;i<ISDN_MAX_CHANNELS;i++) {
-		if(!ippp_table[i]->state)
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if (!ippp_table[i]->state)
 			return i;
 	}
 	return -1;
 }
 
 /*
- * isdn_ppp_open 
+ * isdn_ppp_open
  */
 
-int isdn_ppp_open(int min, struct file *file)
+int
+isdn_ppp_open(int min, struct file *file)
 {
 	int slot;
 	struct ippp_struct *is;
 
+	if (min < 0 || min > ISDN_MAX_CHANNELS)
+		return -ENODEV;
+
 	slot = isdn_ppp_get_slot();
-	if(slot < 0) {
+	if (slot < 0) {
 		return -EBUSY;
 	}
 	is = file->private_data = ippp_table[slot];
 
-	if(is->debug & 0x1)
-		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",slot, min,is->state);
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state);
 
-	is->lp = 0;
-	is->mp_seqno = 0;  /* MP sequence number */
-	is->pppcfg = 0;    /* ppp configuration */
-	is->mpppcfg = 0;   /* mppp configuration */
-	is->range = 0x1000000;	/* MP: 24 bit range */
+	is->lp = NULL;
+	is->mp_seqno = 0;       /* MP sequence number */
+	is->pppcfg = 0;         /* ppp configuration */
+	is->mpppcfg = 0;        /* mppp configuration */
+	is->range = 0x1000000;  /* MP: 24 bit range */
 	is->last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
-	is->unit = -1;	/* set, when we have our interface */
-	is->mru = 1524;	/* MRU, default 1524 */
-	is->maxcid = 16;	/* VJ: maxcid */
+	is->unit = -1;          /* set, when we have our interface */
+	is->mru = 1524;         /* MRU, default 1524 */
+	is->maxcid = 16;        /* VJ: maxcid */
 	is->tk = current;
-	is->wq = NULL;    /* read() wait queue */
-	is->wq1 = NULL;   /* select() wait queue */
-	is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+	is->wq = NULL;          /* read() wait queue */
+	is->wq1 = NULL;         /* select() wait queue */
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
 	is->last = is->rq;
 	is->minor = min;
 #ifdef CONFIG_ISDN_PPP_VJ
-        /*
-         * VJ header compression init
-         */
+	/*
+	 * VJ header compression init
+	 */
 	is->slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
 #endif
 
@@ -310,7 +401,8 @@
 /*
  * release ippp device
  */
-void isdn_ppp_release(int min, struct file *file)
+void
+isdn_ppp_release(int min, struct file *file)
 {
 	int i;
 	struct ippp_struct *is;
@@ -319,15 +411,19 @@
 		return;
 	is = file->private_data;
 
-	if(is->debug & 0x1)
+	if (is->debug & 0x1)
 		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp);
 
-	if (is->lp) {	/* a lp address says: this link is still up */
-		isdn_net_dev *p = dev->netdev;
-		p = is->lp->netdev;
-		is->lp->ppp_slot = -1;
-		isdn_net_hangup(&p->dev); /* lp->ppp_slot==-1 => no calling of isdn_ppp_closewait() */
-		is->lp = NULL;
+	if (is->lp) {           /* a lp address says: this link is still up */
+		isdn_net_dev *p = is->lp->netdev;
+
+		is->state &= ~IPPP_CONNECT;	/* -> effect: no call of wakeup */
+		/*
+		 * isdn_net_hangup() calls isdn_ppp_free()
+		 * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1
+		 * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon()
+		 */
+		isdn_net_hangup(&p->dev);
 	}
 	for (i = 0; i < NUM_RCV_BUFFS; i++) {
 		if (is->rq[i].buf) {
@@ -335,8 +431,8 @@
 			is->rq[i].buf = NULL;
 		}
 	}
-        is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
-        is->last = is->rq;
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
+	is->last = is->rq;
 
 #ifdef CONFIG_ISDN_PPP_VJ
 	slhc_free(is->slcomp);
@@ -346,190 +442,286 @@
 	is->state = 0;
 }
 
-#if 0 /* get_user() / put_user() in 2.1 replace them 1:1 */
 /*
  * get_arg .. ioctl helper
  */
-static int get_arg(void *b, unsigned long *val)
+static int
+get_arg(void *b, void *val, int len)
 {
 	int r;
-	if (copy_from_user((void *) val, b, sizeof(unsigned long))) 
-		return -EFAULT; 
+	if (len <= 0)
+		len = sizeof(unsigned long);
+	if ((r = verify_area(VERIFY_READ, (void *) b, len)))
+		return r;
+	copy_from_user((void *) val, b, len);
 	return 0;
 }
 
 /*
  * set arg .. ioctl helper
  */
-static int set_arg(void *b, unsigned long val)
+static int
+set_arg(void *b, unsigned long val, void *str)
 {
 	int r;
-	if ((r = verify_area(VERIFY_WRITE, b, sizeof(unsigned long))))
-		 return r;
-	copy_to_user(b, (void *) &val, sizeof(unsigned long));
+	if (!str) {
+		if ((r = verify_area(VERIFY_WRITE, b, 4)))
+			return r;
+		copy_to_user(b, (void *) &val, 4);
+	} else {
+		if ((r = verify_area(VERIFY_WRITE, b, val)))
+			return r;
+		copy_to_user(b, str, val);
+	}
 	return 0;
 }
-#endif
 
 /*
- * ippp device ioctl 
+ * ippp device ioctl
  */
-int isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
+int
+isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	unsigned long val;
 	int r;
 	struct ippp_struct *is;
-	unsigned long *argp = (unsigned long*)arg; 
+	isdn_net_local *lp;
 
-	is = file->private_data;
+	is = (struct ippp_struct *) file->private_data;
+	lp = is->lp;
 
-	if(is->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n",min,cmd,is->state);
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state);
 
 	if (!(is->state & IPPP_OPEN))
 		return -EINVAL;
 
 	switch (cmd) {
-	case PPPIOCBUNDLE:
+		case PPPIOCBUNDLE:
 #ifdef CONFIG_ISDN_MPP
-		if ((r = get_user(val, argp)))
-			return r;
-		printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
-                        (int) min, (int) is->unit, (int) val);
-		return isdn_ppp_bundle(is, val);
+			if (!(is->state & IPPP_CONNECT))
+				return -EINVAL;
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+			       (int) min, (int) is->unit, (int) val);
+			return isdn_ppp_bundle(is, val);
 #else
-		return -1;
+			return -1;
 #endif
-		break;
-	case PPPIOCGUNIT:	/* get ppp/isdn unit number */
-		if ((r = put_user(is->unit, argp)))
-			return r;
-		break;
-	case PPPIOCGMPFLAGS:	/* get configuration flags */
-		if ((r = put_user(is->mpppcfg, argp)))
-			return r;
-		break;
-	case PPPIOCSMPFLAGS:	/* set configuration flags */
-		if ((r = get_user(val, argp)))
-			return r;
-		is->mpppcfg = val;
-		break;
-	case PPPIOCGFLAGS:	/* get configuration flags */
-		if ((r = put_user(is->pppcfg ,argp)))
-			return r;
-		break;
-	case PPPIOCSFLAGS:	/* set configuration flags */
-		if ((r = get_user(val, argp))) {
-			return r;
-		}
-		if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP)) {
-			isdn_net_local *lp = is->lp;
-			lp->netdev->dev.tbusy = 0;
-			mark_bh(NET_BH); /* OK .. we are ready to send buffers */
-		}
-		is->pppcfg = val;
-		break;
+			break;
+		case PPPIOCGUNIT:	/* get ppp/isdn unit number */
+			if ((r = set_arg((void *) arg, is->unit, NULL)))
+				return r;
+			break;
+		case PPPIOCGMPFLAGS:	/* get configuration flags */
+			if ((r = set_arg((void *) arg, is->mpppcfg, NULL)))
+				return r;
+			break;
+		case PPPIOCSMPFLAGS:	/* set configuration flags */
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			is->mpppcfg = val;
+			break;
+		case PPPIOCGFLAGS:	/* get configuration flags */
+			if ((r = set_arg((void *) arg, is->pppcfg, NULL)))
+				return r;
+			break;
+		case PPPIOCSFLAGS:	/* set configuration flags */
+			if ((r = get_arg((void *) arg, &val, 0))) {
+				return r;
+			}
+			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
+				if (lp) {
+					lp->netdev->dev.tbusy = 0;
+					mark_bh(NET_BH);	/* OK .. we are ready to send buffers */
+				}
+			}
+			is->pppcfg = val;
+			break;
 #if 0
-	case PPPIOCGSTAT:	/* read PPP statistic information */
-		break;
-	case PPPIOCGTIME:	/* read time delta information */
-		break;
+		case PPPIOCGSTAT:	/* read PPP statistic information */
+			break;
 #endif
-	case PPPIOCSMRU:	/* set receive unit size for PPP */
-		if ((r = get_user(val, argp)))
-			return r;
-		is->mru = val;
-		break;
-	case PPPIOCSMPMRU:
-		break;
-	case PPPIOCSMPMTU:
-		break;
-	case PPPIOCSMAXCID:	/* set the maximum compression slot id */
-		if ((r = get_user(val, argp)))
-			return r;
-		val++;
-		if(is->maxcid != val) {
+		case PPPIOCGIDLE:	/* get idle time information */
+			if (lp) {
+				struct ppp_idle pidle;
+				pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
+				if ((r = set_arg((void *) arg, sizeof(struct ppp_idle), &pidle)))
+					 return r;
+			}
+			break;
+		case PPPIOCSMRU:	/* set receive unit size for PPP */
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			is->mru = val;
+			break;
+		case PPPIOCSMPMRU:
+			break;
+		case PPPIOCSMPMTU:
+			break;
+		case PPPIOCSMAXCID:	/* set the maximum compression slot id */
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			val++;
+			if (is->maxcid != val) {
 #ifdef CONFIG_ISDN_PPP_VJ
-			struct slcompress *sltmp;
+				struct slcompress *sltmp;
 #endif
-			if(is->debug & 0x1)
-				printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n",val);
-			is->maxcid = val;
+				if (is->debug & 0x1)
+					printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val);
+				is->maxcid = val;
 #ifdef CONFIG_ISDN_PPP_VJ
-			sltmp = slhc_init(16,val);
-			if(!sltmp) {
-				printk(KERN_ERR "ippp, can't realloc slhc struct\n");
-				return -ENOMEM;
-			}	
-			if(is->slcomp)
-				slhc_free(is->slcomp);
-			is->slcomp = sltmp;
+				sltmp = slhc_init(16, val);
+				if (!sltmp) {
+					printk(KERN_ERR "ippp, can't realloc slhc struct\n");
+					return -ENOMEM;
+				}
+				if (is->slcomp)
+					slhc_free(is->slcomp);
+				is->slcomp = sltmp;
 #endif
-		}
-		break;
-	case PPPIOCGDEBUG:
-		if ((r = put_user(is->debug, argp)))
-			return r;
-		break;
-	case PPPIOCSDEBUG:
-		if ((r = get_user(val, argp)))
-			return r;
-		is->debug = val;
-		break;
-	default:
-		break;
+			}
+			break;
+		case PPPIOCGDEBUG:
+			if ((r = set_arg((void *) arg, is->debug, 0)))
+				return r;
+			break;
+		case PPPIOCSDEBUG:
+			if ((r = get_arg((void *) arg, &val, 0)))
+				return r;
+			is->debug = val;
+			break;
+		case PPPIOCSCOMPRESS:
+#if 0
+			{
+				struct ppp_option_data pod;
+				r = get_arg((void *) arg, &pod, sizeof(struct ppp_option_data));
+				if (r)
+					return r;
+				ippp_set_compression(is, &pod);
+			}
+#endif
+			break;
+		case PPPIOCGCALLINFO:
+			{
+				struct pppcallinfo pci;
+				memset((char *) &pci,0,sizeof(struct pppcallinfo));
+				if(lp)
+				{
+					strncpy(pci.local_num,lp->msn,63);
+					if(lp->dial) {
+						strncpy(pci.remote_num,lp->dial->num,63);
+					}
+					pci.charge_units = lp->charge;
+					if(lp->outgoing)
+						pci.calltype = CALLTYPE_OUTGOING;
+					else
+						pci.calltype = CALLTYPE_INCOMING;
+					if(lp->flags & ISDN_NET_CALLBACK)
+						pci.calltype |= CALLTYPE_CALLBACK;
+				}
+				return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci);
+			}
+		default:
+			break;
 	}
 	return 0;
 }
 
-int isdn_ppp_select(int min, struct file *file, int type, select_table * st)
+#if (LINUX_VERSION_CODE < 0x020117)
+int
+isdn_ppp_select(int min, struct file *file, int type, select_table * st)
 {
-	struct ippp_buf_queue *bf, *bl;
+	struct ippp_buf_queue *bf,
+	*bl;
 	unsigned long flags;
 	struct ippp_struct *is;
 
 	is = file->private_data;
 
-	if(is->debug & 0x2)
-		printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n",min,type);
+	if (is->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_select: minor: %d, type: %d \n", min, type);
 
 	if (!(is->state & IPPP_OPEN))
 		return -EINVAL;
 
 	switch (type) {
-	case SEL_IN:
-		save_flags(flags);
-		cli();
-		bl = is->last;
-		bf = is->first;
-		/* 
-		 * if IPPP_NOBLOCK is set we return even if we have nothing to read 
-		 */
-		if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) {
-			select_wait(&is->wq, st);
+		case SEL_IN:
+			save_flags(flags);
+			cli();
+			bl = is->last;
+			bf = is->first;
+			/*
+			 * if IPPP_NOBLOCK is set we return even if we have nothing to read
+			 */
+			if (bf->next == bl && !(is->state & IPPP_NOBLOCK)) {
+				select_wait(&is->wq, st);
+				restore_flags(flags);
+				return 0;
+			}
+			is->state &= ~IPPP_NOBLOCK;
 			restore_flags(flags);
+			return 1;
+		case SEL_OUT:
+			/* we're always ready to send .. */
+			return 1;
+		case SEL_EX:
+			select_wait(&is->wq1, st);
 			return 0;
-		}
-		is->state &= ~IPPP_NOBLOCK;
-		restore_flags(flags);
-		return 1;
-	case SEL_OUT:
-                /* we're always ready to send .. */
-		return 1;
-	case SEL_EX:
-		select_wait(&is->wq1, st);
-		return 0;
 	}
 	return 1;
 }
+#else
+unsigned int
+isdn_ppp_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask;
+	struct ippp_buf_queue *bf;
+	struct ippp_buf_queue *bl;
+	unsigned long flags;
+	struct ippp_struct *is;
+
+	is = file->private_data;
+
+	if (is->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", MINOR(file->f_inode->i_rdev));
+
+	poll_wait(&is->wq, wait);
+
+	if (!(is->state & IPPP_OPEN)) {
+		printk(KERN_DEBUG "isdn_ppp: device not open\n");
+		return POLLERR;
+	}
+	/* we're always ready to send .. */
+	mask = POLLOUT | POLLWRNORM;
+
+	save_flags(flags);
+	cli();
+	bl = is->last;
+	bf = is->first;
+	/*
+	 * if IPPP_NOBLOCK is set we return even if we have nothing to read
+	 */
+	if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
+		is->state &= ~IPPP_NOBLOCK;
+		mask |= POLLIN | POLLRDNORM;
+	}
+	restore_flags(flags);
+	return mask;
+}
+#endif
+
 
 /*
  *  fill up isdn_ppp_read() queue ..
  */
 
-static int isdn_ppp_fill_rq(unsigned char *buf, int len,int proto, int slot)
+static int
+isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
 {
-	struct ippp_buf_queue *bf, *bl;
+	struct ippp_buf_queue *bf,
+	*bl;
 	unsigned long flags;
 	unsigned char *nbuf;
 	struct ippp_struct *is;
@@ -544,9 +736,8 @@
 		printk(KERN_DEBUG "ippp: device not activated.\n");
 		return 0;
 	}
-
-	nbuf = (unsigned char *) kmalloc(len+4, GFP_ATOMIC);
-	if(!nbuf) {
+	nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
+	if (!nbuf) {
 		printk(KERN_WARNING "ippp: Can't alloc buf\n");
 		return 0;
 	}
@@ -554,7 +745,7 @@
 	nbuf[1] = PPP_UI;
 	nbuf[2] = proto >> 8;
 	nbuf[3] = proto & 0xff;
-	memcpy(nbuf+4, buf, len);
+	memcpy(nbuf + 4, buf, len);
 
 	save_flags(flags);
 	cli();
@@ -569,7 +760,7 @@
 		is->first = bf;
 	}
 	bl->buf = (char *) nbuf;
-	bl->len = len+4;
+	bl->len = len + 4;
 
 	is->last = bl->next;
 	restore_flags(flags);
@@ -585,10 +776,12 @@
  *           reports, that there is data
  */
 
-int isdn_ppp_read(int min, struct file *file, char *buf, int count)
+int
+isdn_ppp_read(int min, struct file *file, char *buf, int count)
 {
 	struct ippp_struct *is;
 	struct ippp_buf_queue *b;
+	int r;
 	unsigned long flags;
 
 	is = file->private_data;
@@ -596,6 +789,9 @@
 	if (!(is->state & IPPP_OPEN))
 		return 0;
 
+	if ((r = verify_area(VERIFY_WRITE, (void *) buf, count)))
+		return r;
+
 	save_flags(flags);
 	cli();
 
@@ -606,8 +802,7 @@
 	}
 	if (b->len < count)
 		count = b->len;
-	if (copy_to_user(buf, b->buf, count)) 
-		count = -EFAULT; /* ugly */
+	copy_to_user(buf, b->buf, count);
 	kfree(b->buf);
 	b->buf = NULL;
 	is->first = b;
@@ -619,11 +814,14 @@
 /*
  * ipppd wanna write a packet to the card .. non-blocking
  */
- 
-int isdn_ppp_write(int min, struct file *file,  const char *buf, int count)
+
+int
+isdn_ppp_write(int min, struct file *file, const char *buf, int count)
 {
 	isdn_net_local *lp;
 	struct ippp_struct *is;
+	int proto;
+	unsigned char protobuf[4];
 
 	is = file->private_data;
 
@@ -637,49 +835,64 @@
 	if (!lp)
 		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
 	else {
-		lp->huptimer = 0;
+		/*
+		 * Don't reset huptimer for
+		 * LCP packets. (Echo requests).
+		 */
+		copy_from_user(protobuf, buf, 4);
+		proto = PPP_PROTOCOL(protobuf);
+		if (proto != PPP_LCP)
+			lp->huptimer = 0;
+
 		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
 			return 0;
 
 		if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
 		    (lp->flags & ISDN_NET_CONNECTED)) {
+			int cnt;
 			struct sk_buff *skb;
 			skb = dev_alloc_skb(count);
-			if(!skb) {
+			if (!skb) {
 				printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
 				return count;
 			}
-			skb->free = 1;
+			SET_SKB_FREE(skb);
 			copy_from_user(skb_put(skb, count), buf, count);
-			if(isdn_writebuf_skb_stub(lp->isdn_device,lp->isdn_channel,skb) != count) {
-				if(lp->sav_skb) {
-					dev_kfree_skb(lp->sav_skb,FREE_WRITE);
-					printk(KERN_INFO "isdn_ppp_write: freeing sav_skb!\n");
-				}
+			if (is->debug & 0x40) {
+				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
+				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+			}
+			if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, skb)) != count) {
+				if (lp->sav_skb) {
+					dev_kfree_skb(lp->sav_skb, FREE_WRITE);
+					printk(KERN_INFO "isdn_ppp_write: freeing sav_skb (%d,%d)!\n", cnt, count);
+				} else
+					printk(KERN_INFO "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n", cnt, count);
 				lp->sav_skb = skb;
 			}
 		}
 	}
-
 	return count;
 }
 
 /*
- * init memory, structures etc. 
+ * init memory, structures etc.
  */
 
-int isdn_ppp_init(void)
+int
+isdn_ppp_init(void)
 {
-	int i, j;
+	int i,
+	 j;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-	        if (!(ippp_table[i] = (struct ippp_struct *)
-	                kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
-		        printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+		if (!(ippp_table[i] = (struct ippp_struct *)
+		      kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
+			printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
 			for (j = 0; j < i; j++)
 				kfree(ippp_table[i]);
-		        return -1;
-	        }
+			return -1;
+		}
 		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
 		ippp_table[i]->state = 0;
 		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
@@ -695,70 +908,72 @@
 	return 0;
 }
 
-void isdn_ppp_cleanup(void)
+void
+isdn_ppp_cleanup(void)
 {
-        int i;
+	int i;
 
-        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-                kfree(ippp_table[i]);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		kfree(ippp_table[i]);
 }
 
 /*
  * handler for incoming packets on a syncPPP interface
  */
-
-void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
+void
+isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
 {
 	struct ippp_struct *is;
 	is = ippp_table[lp->ppp_slot];
 
-	if(is->debug & 0x4)
-		printk(KERN_DEBUG "recv skb, len: %ld\n",skb->len);
-
-	if(net_dev->local.master) {
+	if (is->debug & 0x4) {
+		printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len);
+		isdn_ppp_frame_log("receive", skb->data, skb->len, 32);
+	}
+	if (net_dev->local.master) {
 		printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
-		net_dev = ((isdn_net_local*) net_dev->local.master->priv)->netdev;
+		net_dev = ((isdn_net_local *) net_dev->local.master->priv)->netdev;
 	}
-
-	if(skb->data[0] == 0xff && skb->data[1] == 0x03)
-		skb_pull(skb,2);
+	if (skb->data[0] == 0xff && skb->data[1] == 0x03)
+		skb_pull(skb, 2);
 	else if (is->pppcfg & SC_REJ_COMP_AC) {
-		skb->free = 1;
-		dev_kfree_skb(skb,0 /* FREE_READ */ );
-		return;		/* discard it silently */
+		SET_SKB_FREE(skb);
+		dev_kfree_skb(skb, 0 /* FREE_READ */ );
+		return;         /* discard it silently */
 	}
-
 #ifdef CONFIG_ISDN_MPP
 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
 		int proto;
 		int sqno_end;
 		if (skb->data[0] & 0x1) {
 			proto = skb->data[0];
-			skb_pull(skb,1);	/* protocol ID is only 8 bit */
+			skb_pull(skb, 1);	/* protocol ID is only 8 bit */
 		} else {
 			proto = ((int) skb->data[0] << 8) + skb->data[1];
-			skb_pull(skb,2);
+			skb_pull(skb, 2);
 		}
 		if (proto == PPP_MP) {
 			isdn_net_local *lpq;
-			int sqno, min_sqno, tseq;
+			long sqno,
+			 min_sqno,
+			 tseq;
 			u_char BEbyte = skb->data[0];
-			if(is->debug & 0x8)
-	 			printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto ,
-					(int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], 
-					(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+			if (is->debug & 0x8)
+				printk(KERN_DEBUG "recv: %d/%04x/%d -> %02x %02x %02x %02x %02x %02x\n", lp->ppp_slot, proto,
+				       (int) skb->len, (int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
+				       (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
 			if (!(is->mpppcfg & SC_IN_SHORT_SEQ)) {
 				sqno = ((int) skb->data[1] << 16) + ((int) skb->data[2] << 8) + (int) skb->data[3];
-				skb_pull(skb,4);
+				skb_pull(skb, 4);
 			} else {
 				sqno = (((int) skb->data[0] & 0xf) << 8) + (int) skb->data[1];
-				skb_pull(skb,2);
+				skb_pull(skb, 2);
 			}
 
 			if ((tseq = is->last_link_seqno) >= sqno) {
 				int range = is->range;
-				if (tseq + 1024 < range + sqno) /* redundancy check .. not MP conform */
-					printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %d, last: %d !!!\n", sqno, tseq);
+				if (tseq + 1024 < range + sqno)	/* redundancy check .. not MP conform */
+					printk(KERN_WARNING "isdn_ppp_receive, MP, detected overflow with sqno: %ld, last: %ld !!!\n", sqno, tseq);
 				else {
 					sqno += range;
 					is->last_link_seqno = sqno;
@@ -766,16 +981,19 @@
 			} else
 				is->last_link_seqno = sqno;
 
-			for (min_sqno = 0, lpq = net_dev->queue;;) {
-				if (ippp_table[lpq->ppp_slot]->last_link_seqno > min_sqno)
-					min_sqno = ippp_table[lpq->ppp_slot]->last_link_seqno;
+			for (min_sqno = LONG_MAX, lpq = net_dev->queue;;) {
+				long lls = ippp_table[lpq->ppp_slot]->last_link_seqno;
+				if (lls >= 0 && lls < min_sqno)
+					min_sqno = lls;
 				lpq = lpq->next;
 				if (lpq == net_dev->queue)
 					break;
 			}
 			if (min_sqno >= ippp_table[lpq->ppp_slot]->range) {	/* OK, every link overflowed */
 				int mask = ippp_table[lpq->ppp_slot]->range - 1;	/* range is a power of 2 */
+#if 0
 				isdn_ppp_cleanup_queue(net_dev, min_sqno);
+#endif
 				isdn_ppp_mask_queue(net_dev, mask);
 				net_dev->ib.next_num &= mask;
 				{
@@ -794,12 +1012,23 @@
 				}
 			}
 			if ((BEbyte & (MP_BEGIN_FRAG | MP_END_FRAG)) != (MP_BEGIN_FRAG | MP_END_FRAG)) {
-				printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
-				if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb , BEbyte, &sqno, min_sqno)) < 0)
-					return;		/* no packet complete */
+				static int dmes = 0;
+				if( !dmes ) {
+					printk(KERN_DEBUG "ippp: trying ;) to fill mp_queue %d .. UNTESTED!!\n", lp->ppp_slot);
+					dmes = 1;
+				}
+				if ((sqno_end = isdn_ppp_fill_mpqueue(net_dev, &skb, BEbyte, &sqno, min_sqno)) < 0) {
+					net_dev->ib.modify = 1;	/* block timeout-timer */
+					isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
+					net_dev->ib.modify = 0;
+					return;	/* no packet complete */
+				}
 			} else
 				sqno_end = sqno;
 
+			if (is->debug & 0x40)
+				printk(KERN_DEBUG "min_sqno: %ld sqno_end %d next: %ld\n", min_sqno, sqno_end, net_dev->ib.next_num);
+
 			/*
 			 * MP buffer management .. reorders incoming packets ..
 			 * lotsa mem-copies and not heavily tested.
@@ -807,7 +1036,7 @@
 			 * first check whether there is more than one link in the bundle
 			 * then check whether the number is in order
 			 */
-			net_dev->ib.modify = 1;		/* block timeout-timer */
+			net_dev->ib.modify = 1;	/* block timeout-timer */
 			if (net_dev->ib.bundled && net_dev->ib.next_num != sqno) {
 				/*
 				 * packet is not 'in order'
@@ -818,9 +1047,9 @@
 				if (!q) {
 					net_dev->ib.modify = 0;
 					printk(KERN_WARNING "ippp/MPPP: Bad! Can't alloc sq node!\n");
-					skb->free = 1;
+					SET_SKB_FREE(skb);
 					dev_kfree_skb(skb, 0 /* FREE_READ */ );
-					return;		/* discard */
+					return;	/* discard */
 				}
 				q->skb = skb;
 				q->sqno_end = sqno_end;
@@ -842,136 +1071,123 @@
 						ql->next = q;
 					}
 				}
-				net_dev->ib.modify = 0;
-				return;
 			} else {
-				/* 
-			 	 * packet was 'in order' .. push it higher
+				/*
+				 * packet was 'in order' .. push it higher
 				 */
-				struct sqqueue *q;
-
 				net_dev->ib.next_num = sqno_end + 1;
 				isdn_ppp_push_higher(net_dev, lp, skb, -1);
-
-                                /*
-                                 * check queue, whether we have still buffered the next packet(s)
-                                 */
-				while ((q = net_dev->ib.sq) && q->sqno_start == net_dev->ib.next_num) {
-					isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
-					net_dev->ib.sq = q->next;
-					net_dev->ib.next_num = q->sqno_end + 1;
-					kfree(q);
-				}
 			}
+			isdn_ppp_cleanup_sqqueue(net_dev, lp, min_sqno);
 			net_dev->ib.modify = 0;
 
 		} else
-			isdn_ppp_push_higher(net_dev, lp, skb , proto);
+			isdn_ppp_push_higher(net_dev, lp, skb, proto);
 	} else
 #endif
-		isdn_ppp_push_higher(net_dev, lp, skb , -1);
+		isdn_ppp_push_higher(net_dev, lp, skb, -1);
 }
 
-
 /*
  * push frame to higher layers
  * note: net_dev has to be master net_dev
  */
-static void isdn_ppp_push_higher(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb,int proto)
+static void
+isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
 {
 	struct device *dev = &net_dev->dev;
 	struct ippp_struct *is = ippp_table[lp->ppp_slot];
 
-	if (proto < 0) {	/* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
+	if (proto < 0) {        /* MP, oder normales Paket bei REJ_MP, MP Pakete gehen bei REJ zum pppd */
 		if (skb->data[0] & 0x01) {	/* is it odd? */
 			proto = (unsigned char) skb->data[0];
-			skb_pull(skb,1);	/* protocol ID is only 8 bit */
+			skb_pull(skb, 1);	/* protocol ID is only 8 bit */
 		} else {
 			proto = ((int) (unsigned char) skb->data[0] << 8) + (unsigned char) skb->data[1];
-			skb_pull(skb,2);
+			skb_pull(skb, 2);
 		}
 	}
-
-	if(is->debug & 0x10)
-		printk(KERN_DEBUG "push, skb %ld %04x\n",skb->len,proto);
-
+	if (is->debug & 0x10) {
+		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
+		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32);
+	}
 	switch (proto) {
-	case PPP_IPX: /* untested */
-		if(is->debug & 0x20)
-			printk(KERN_DEBUG "isdn_ppp: _IPX\n");
-		skb->dev = dev;
-		skb->mac.raw = skb->data;
-		skb->protocol = htons(ETH_P_IPX);
-		break;
-#ifdef CONFIG_ISDN_PPP_VJ
-	case PPP_VJC_UNCOMP:
-		if(is->debug & 0x20)
-			printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
-		if(slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
-			printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
-			net_dev->local.stats.rx_dropped++;
-			skb->free = 1;
-			dev_kfree_skb(skb,0 /* FREE_READ */ );
-			return;
-		}
-#endif
-	case PPP_IP:
-		if(is->debug & 0x20)
-			printk(KERN_DEBUG "isdn_ppp: IP\n");
-		skb->dev = dev;
-		skb->mac.raw = skb->data;
-		skb->protocol = htons(ETH_P_IP);
-		break;
-	case PPP_VJC_COMP:
-                if(is->debug & 0x20)
-                        printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+		case PPP_IPX:  /* untested */
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IPX\n");
+			skb->dev = dev;
+			skb->mac.raw = skb->data;
+			skb->protocol = htons(ETH_P_IPX);
+			break;
 #ifdef CONFIG_ISDN_PPP_VJ
-		{
-			struct sk_buff *skb_old = skb;
-			int pkt_len;
-			skb = dev_alloc_skb(skb_old->len + 40);
-
-			skb_old->free = 1;
-
-			if (!skb) {
-				printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+		case PPP_VJC_UNCOMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
+			if (slhc_remember(ippp_table[net_dev->local.ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+				printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
 				net_dev->local.stats.rx_dropped++;
-				dev_kfree_skb(skb_old,0 /* FREE_READ */ );
+				SET_SKB_FREE(skb);
+				dev_kfree_skb(skb, 0 /* FREE_READ */ );
 				return;
 			}
+#endif
+		case PPP_IP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IP\n");
 			skb->dev = dev;
-			skb_put(skb,skb_old->len + 40);
-			memcpy(skb->data, skb_old->data, skb_old->len);
 			skb->mac.raw = skb->data;
-			pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp,
-						  skb->data, skb_old->len);
-			dev_kfree_skb(skb_old,0 /* FREE_READ */ );
-			if(pkt_len < 0) {
-				skb->free = 1;
-				dev_kfree_skb(skb, 0 /* FREE_READ */ );
-				lp->stats.rx_dropped++;
-				return;
-			}
-			skb_trim(skb, pkt_len);
 			skb->protocol = htons(ETH_P_IP);
-		}
+			break;
+		case PPP_VJC_COMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+#ifdef CONFIG_ISDN_PPP_VJ
+			{
+				struct sk_buff *skb_old = skb;
+				int pkt_len;
+				skb = dev_alloc_skb(skb_old->len + 40);
+
+				SET_SKB_FREE(skb_old);
+
+				if (!skb) {
+					printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+					net_dev->local.stats.rx_dropped++;
+					dev_kfree_skb(skb_old, 0 /* FREE_READ */ );
+					return;
+				}
+				skb->dev = dev;
+				skb_put(skb, skb_old->len + 40);
+				memcpy(skb->data, skb_old->data, skb_old->len);
+				skb->mac.raw = skb->data;
+				pkt_len = slhc_uncompress(ippp_table[net_dev->local.ppp_slot]->slcomp,
+						skb->data, skb_old->len);
+				dev_kfree_skb(skb_old, 0 /* FREE_READ */ );
+				if (pkt_len < 0) {
+					SET_SKB_FREE(skb);
+					dev_kfree_skb(skb, 0 /* FREE_READ */ );
+					lp->stats.rx_dropped++;
+					return;
+				}
+				skb_trim(skb, pkt_len);
+				skb->protocol = htons(ETH_P_IP);
+			}
 #else
-		printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
-		lp->stats.rx_dropped++;
-		skb->free = 1;
-		dev_kfree_skb(skb,0 /* FREE_READ */ );
-		return;
+			printk(KERN_INFO "isdn: Ooopsa .. VJ-Compression support not compiled into isdn driver.\n");
+			lp->stats.rx_dropped++;
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, 0 /* FREE_READ */ );
+			return;
 #endif
-		break;
-	default:
-		isdn_ppp_fill_rq(skb->data, skb->len,proto, lp->ppp_slot);	/* push data to pppd device */
-		skb->free = 1;
-		dev_kfree_skb(skb,0 /* FREE_READ */ );
-		return;
+			break;
+		default:
+			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
+			SET_SKB_FREE(skb);
+			dev_kfree_skb(skb, 0 /* FREE_READ */ );
+			return;
 	}
 
 	netif_rx(skb);
-	net_dev->local.stats.rx_packets++;
+	/* net_dev->local.stats.rx_packets++; *//* done in isdn_net.c */
 	/* Reset hangup-timer */
 	lp->huptimer = 0;
 
@@ -979,56 +1195,77 @@
 }
 
 /*
- * send ppp frame .. we expect a PIDCOMPressable proto -- 
+ * send ppp frame .. we expect a PIDCOMPressable proto --
  *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
  *
  * VJ compression may change skb pointer!!! .. requeue with old
- * skb isn't allowed!! 
+ * skb isn't allowed!!
  */
 
-static void isdn_ppp_skb_destructor(struct sk_buff *skb)
+int
+isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
 {
-	char outstr[80],*outpnt=outstr;
-	int i;
-
-	*outpnt = 0;
-	for(i=0;i<24 && i<skb->len;i++) {
-		sprintf(outpnt,"%02x ",skb->data[i]);
-		outpnt += 3;
-	}
-	printk(KERN_DEBUG "ippp_dstrct: %s\n",outstr);
-}
-
-int isdn_ppp_xmit(struct sk_buff *skb, struct device *dev)
-{
-	struct device *mdev = ((isdn_net_local *) (dev->priv) )->master;	/* get master (for redundancy) */
-	isdn_net_local *lp,*mlp;
+	struct device *mdev = ((isdn_net_local *) (dev->priv))->master;	/* get master (for redundancy) */
+	isdn_net_local *lp,
+	*mlp;
 	isdn_net_dev *nd;
-	int proto = PPP_IP;	/* 0x21 */
-	struct ippp_struct *ipt,*ipts;
+	int proto = PPP_IP;     /* 0x21 */
+	struct ippp_struct *ipt,
+	*ipts;
 
-	if(mdev)
-		mlp = (isdn_net_local *) (mdev->priv); 
-	else
+	if (mdev)
+		mlp = (isdn_net_local *) (mdev->priv);
+	else {
+		mdev = dev;
 		mlp = (isdn_net_local *) (dev->priv);
-	nd = mlp->netdev;	/* get master lp */
+	}
+	nd = mlp->netdev;       /* get master lp */
 	ipts = ippp_table[mlp->ppp_slot];
 
-	if (!(ipts->pppcfg & SC_ENABLE_IP)) {    /* PPP connected ? */
-		if(ipts->debug & 0x1) {
-			printk(KERN_INFO "%s: IP frame delayed.\n",dev->name);
-			skb->destructor = isdn_ppp_skb_destructor;
-		}
- 		return 1;
-        }
+	if (!(ipts->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
+#ifdef ISDN_SYNCPPP_READDRESS
+		if (!ipts->old_pa_addr)
+			ipts->old_pa_addr = mdev->pa_addr;
+		if (!ipts->old_pa_dstaddr)
+			ipts->old_pa_dstaddr = mdev->pa_dstaddr;
+#endif
+		if (ipts->debug & 0x1)
+			printk(KERN_INFO "%s: IP frame delayed.\n", dev->name);
+		return 1;
+	}
+	switch (ntohs(skb->protocol)) {
+		case ETH_P_IP:
+			proto = PPP_IP;
+#ifdef ISDN_SYNCPPP_READDRESS
+			if (ipts->old_pa_addr != mdev->pa_addr) {
+				struct iphdr *ipfr;
+				ipfr = (struct iphdr *) skb->data;
+				printk(KERN_DEBUG "IF-address changed from %lx to %lx\n", ipts->old_pa_addr, mdev->pa_addr);
+				if (ipfr->version == 4) {
+					if (ipfr->saddr == ipts->old_pa_addr) {
+						printk(KERN_DEBUG "readdressing %lx to %lx\n", ipfr->saddr, mdev->pa_addr);
+						ipfr->saddr = mdev->pa_addr;
+					}
+				}
+			}
+			/* dstaddr change not so improtant */
+#endif
+			break;
+		case ETH_P_IPX:
+			proto = PPP_IPX;	/* untested */
+			break;
+		default:
+			dev_kfree_skb(skb, FREE_WRITE);
+			printk(KERN_ERR "isdn_ppp: skipped frame with unsupported protocoll: %#x.\n", skb->protocol);
+			return 0;
+	}
 
-	skb->destructor = NULL;
+	lp = nd->queue;         /* get lp on top of queue */
 
-	lp = nd->queue;		/* get lp on top of queue */
-	if(lp->sav_skb) {	/* find a non-busy device */
+	if (lp->sav_skb) {      /* find a non-busy device */
 		isdn_net_local *nlp = lp->next;
-		while(lp->sav_skb) {
-			if(lp == nlp)
+		while (lp->sav_skb) {
+			if (lp == nlp)
 				return 1;
 			nlp = nd->queue = nd->queue->next;
 		}
@@ -1036,43 +1273,42 @@
 	}
 	ipt = ippp_table[lp->ppp_slot];
 
-        lp->huptimer = 0;
+	lp->huptimer = 0;
 
 	/*
-	 * after this line .. requeueing in the device queue is no longer allowed!!! 
+	 * after this line .. requeueing in the device queue is no longer allowed!!!
 	 */
 
-	if(ipt->debug & 0x4)
-		printk(KERN_DEBUG  "xmit skb, len %ld\n",skb->len);
+	if (ipt->debug & 0x4)
+		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
 
 #ifdef CONFIG_ISDN_PPP_VJ
-	if (ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes .. but this check again */
+	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes .. but this check again */
 		struct sk_buff *new_skb;
 
 		new_skb = dev_alloc_skb(skb->len);
-		if(new_skb) {
+		if (new_skb) {
 			u_char *buf;
 			int pktlen;
 
 			new_skb->dev = skb->dev;
-			new_skb->free = 1;
-			skb_put(new_skb,skb->len);
+			SET_SKB_FREE(new_skb);
+			skb_put(new_skb, skb->len);
 			buf = skb->data;
 
 			pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
-					&buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+				 &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
 
-			if(buf != skb->data) { /* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*)  */
-				if(new_skb->data != buf)
+			if (buf != skb->data) {	/* copied to new buffer ??? (btw: WHY must slhc copy it?? *sigh*)  */
+				if (new_skb->data != buf)
 					printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
-				dev_kfree_skb(skb,FREE_WRITE);
+				dev_kfree_skb(skb, FREE_WRITE);
 				skb = new_skb;
-			}
-			else {
-				dev_kfree_skb(new_skb,0 /* FREE_WRITE */ );
+			} else {
+				dev_kfree_skb(new_skb, 0 /* FREE_WRITE */ );
 			}
 
-			skb_trim(skb,pktlen);
+			skb_trim(skb, pktlen);
 			if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) {	/* cslip? style -> PPP */
 				proto = PPP_VJC_COMP;
 				skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
@@ -1085,8 +1321,8 @@
 	}
 #endif
 
-        if(ipt->debug & 0x24)
- 		printk(KERN_DEBUG  "xmit2 skb, len %ld, proto %04x\n",skb->len,proto);
+	if (ipt->debug & 0x24)
+		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
 
 #ifdef CONFIG_ISDN_MPP
 	if (ipt->mpppcfg & SC_MP_PROT) {
@@ -1111,34 +1347,41 @@
 		proto = PPP_MP; /* MP Protocol, 0x003d */
 	}
 #endif
-	skb_push(skb,4);
-	skb->data[0] = 0xff;        /* All Stations */
-	skb->data[1] = 0x03;        /* Unnumbered information */
+	skb_push(skb, 4);
+	skb->data[0] = 0xff;    /* All Stations */
+	skb->data[1] = 0x03;    /* Unnumbered information */
 	skb->data[2] = proto >> 8;
 	skb->data[3] = proto & 0xff;
 
-        /* tx-stats are now updated via BSENT-callback */
-	if(isdn_net_send_skb(dev , lp , skb)) {	
-		if(lp->sav_skb) {	/* whole sav_skb processing with disabled IRQs ?? */
-			printk(KERN_ERR "%s: whoops .. there is another stored skb!\n",dev->name);
-			dev_kfree_skb(skb,FREE_WRITE);
-		}
-		else
+	/* tx-stats are now updated via BSENT-callback */
+
+	if (ipts->debug & 0x40) {
+		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
+		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+	}
+	if (isdn_net_send_skb(dev, lp, skb)) {
+		if (lp->sav_skb) {	/* whole sav_skb processing with disabled IRQs ?? */
+			printk(KERN_ERR "%s: whoops .. there is another stored skb!\n", dev->name);
+			dev_kfree_skb(skb, FREE_WRITE);
+		} else
 			lp->sav_skb = skb;
 	}
 	return 0;
 }
 
-void isdn_ppp_free_sqqueue(isdn_net_dev * p) 
+#ifdef CONFIG_ISDN_MPP
+
+static void
+isdn_ppp_free_sqqueue(isdn_net_dev * p)
 {
 	struct sqqueue *q = p->ib.sq;
 
 	p->ib.sq = NULL;
-	while(q) {
+	while (q) {
 		struct sqqueue *qn = q->next;
-		if(q->skb) {
-			q->skb->free = 1;
-			dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+		if (q->skb) {
+			SET_SKB_FREE(q->skb);
+			dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 		}
 		kfree(q);
 		q = qn;
@@ -1146,26 +1389,29 @@
 
 }
 
-void isdn_ppp_free_mpqueue(isdn_net_dev * p)
+static void 
+isdn_ppp_free_mpqueue(isdn_net_dev * p)
 {
-	struct mpqueue *ql, *q = p->mp_last;
+	struct mpqueue *q = p->mp_last;
+	p->mp_last = NULL;
+
 	while (q) {
-		ql = q->next;
-		q->skb->free = 1;
- 		dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+		struct mpqueue *ql = q->next;
+		SET_SKB_FREE(q->skb);
+		dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 		kfree(q);
 		q = ql;
 	}
 }
 
-#ifdef CONFIG_ISDN_MPP
-
-static int isdn_ppp_bundle(struct ippp_struct *is, int unit)
+static int
+isdn_ppp_bundle(struct ippp_struct *is, int unit)
 {
 	char ifn[IFNAMSIZ + 1];
 	long flags;
 	isdn_net_dev *p;
-	isdn_net_local *lp,*nlp;
+	isdn_net_local *lp,
+	*nlp;
 
 	sprintf(ifn, "ippp%d", unit);
 	p = isdn_net_findif(ifn);
@@ -1206,7 +1452,8 @@
 }
 
 
-static void isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
+static void
+isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
 {
 	struct mpqueue *q = dev->mp_last;
 	while (q) {
@@ -1215,20 +1462,24 @@
 	}
 }
 
-
-static int isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff ** skb, int BEbyte, int *sqnop, int min_sqno)
+static int
+isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long *sqnop, int min_sqno)
 {
-	struct mpqueue *qe, *q1, *q;
-	long cnt, flags;
-	int pktlen, sqno_end;
+	struct mpqueue *qe,
+	*q1,
+	*q;
+	long cnt,
+	 flags;
+	int pktlen,
+	 sqno_end;
 	int sqno = *sqnop;
 
-	q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_KERNEL);
+	q1 = (struct mpqueue *) kmalloc(sizeof(struct mpqueue), GFP_ATOMIC);
 	if (!q1) {
 		printk(KERN_WARNING "isdn_ppp_fill_mpqueue: Can't alloc struct memory.\n");
 		save_flags(flags);
 		cli();
-		isdn_ppp_cleanup_queue(dev, min_sqno);
+		isdn_ppp_cleanup_mpqueue(dev, min_sqno);
 		restore_flags(flags);
 		return -1;
 	}
@@ -1244,11 +1495,11 @@
 		dev->mp_last = q1;
 		q1->next = NULL;
 		q1->last = NULL;
-		isdn_ppp_cleanup_queue(dev, min_sqno);	/* not necessary */
+		isdn_ppp_cleanup_mpqueue(dev, min_sqno);	/* not necessary */
 		restore_flags(flags);
-		return -1;
+		return -1;	/* -1 is not an error. Just says, that this fragment hasn't complete a full frame */
 	}
-	for (;;) {		/* the faster way would be to step from the queue-end to the start */
+	for (;;) {              /* the faster way would be to step from the queue-end to the start */
 		if (sqno > q->sqno) {
 			if (q->next) {
 				q = q->next;
@@ -1278,7 +1529,7 @@
 	while (!(q->BEbyte & MP_END_FRAG)) {
 		cnt++;
 		if (!(q->next) || q->next->sqno != cnt) {
-			isdn_ppp_cleanup_queue(dev, min_sqno);
+			isdn_ppp_cleanup_mpqueue(dev, min_sqno);
 			restore_flags(flags);
 			return -1;
 		}
@@ -1293,7 +1544,7 @@
 	while (!(q->BEbyte & MP_BEGIN_FRAG)) {
 		cnt--;
 		if (!(q->last) || q->last->sqno != cnt) {
-			isdn_ppp_cleanup_queue(dev, min_sqno);
+			isdn_ppp_cleanup_mpqueue(dev, min_sqno);
 			restore_flags(flags);
 			return -1;
 		}
@@ -1313,29 +1564,29 @@
 	sqno_end = qe->sqno;
 	*sqnop = q->sqno;
 
-	isdn_ppp_cleanup_queue(dev, min_sqno);
+	isdn_ppp_cleanup_mpqueue(dev, min_sqno);
 	restore_flags(flags);
 
-	*skb = dev_alloc_skb(pktlen + 40); /* not needed: +40 for VJ compression .. */
+	*skb = dev_alloc_skb(pktlen + 40);	/* not needed: +40 for VJ compression .. */
 
 	if (!(*skb)) {
 		while (q) {
 			struct mpqueue *ql = q->next;
-			q->skb->free = 1;
-			dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+			SET_SKB_FREE(q->skb);
+			dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 			kfree(q);
 			q = ql;
 		}
 		return -2;
 	}
 	cnt = 0;
-	skb_put(*skb,pktlen);
+	skb_put(*skb, pktlen);
 	while (q) {
 		struct mpqueue *ql = q->next;
 		memcpy((*skb)->data + cnt, q->skb->data, q->skb->len);
 		cnt += q->skb->len;
-		q->skb->free = 1;
-		dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+		SET_SKB_FREE(q->skb);
+		dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 		kfree(q);
 		q = ql;
 	}
@@ -1344,10 +1595,34 @@
 }
 
 /*
+ * check sq-queue, whether we have still buffered the next packet(s)
+ * or packets with a sqno less or equal to min_sqno
+ * net_dev: master netdevice , lp: 'real' local connection
+ */
+static void
+isdn_ppp_cleanup_sqqueue(isdn_net_dev * net_dev, isdn_net_local * lp, long min_sqno)
+{
+	struct sqqueue *q;
+
+	while ((q = net_dev->ib.sq) && (q->sqno_start == net_dev->ib.next_num || q->sqno_end <= min_sqno)) {
+		if (q->sqno_start != net_dev->ib.next_num) {
+			printk(KERN_DEBUG "ippp: MP, stepping over missing frame: %ld\n", net_dev->ib.next_num);
+#ifdef CONFIG_ISDN_PPP_VJ
+			slhc_toss(ippp_table[net_dev->local.ppp_slot]->slcomp);
+#endif
+		}
+		isdn_ppp_push_higher(net_dev, lp, q->skb, -1);
+		net_dev->ib.sq = q->next;
+		net_dev->ib.next_num = q->sqno_end + 1;
+		kfree(q);
+	}
+}
+
+/*
  * remove stale packets from list
  */
-
-static void isdn_ppp_cleanup_queue(isdn_net_dev * dev, long min_sqno)
+static void
+isdn_ppp_cleanup_mpqueue(isdn_net_dev * dev, long min_sqno)
 {
 #ifdef CONFIG_ISDN_PPP_VJ
 	int toss = 0;
@@ -1355,11 +1630,12 @@
 /* z.z einfaches aussortieren gammeliger pakete. Fuer die Zukunft:
    eventuell, solange vorne kein B-paket ist und sqno<=min_sqno: auch rauswerfen
    wenn sqno<min_sqno und Luecken vorhanden sind: auch weg (die koennen nicht mehr gefuellt werden)
-   bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete 
+   bei paketen groesser min_sqno: ueber mp_mrru: wenn summe ueber pktlen der rumhaengenden Pakete
    groesser als mrru ist: raus damit , Pakete muessen allerdings zusammenhaengen sonst koennte
    ja ein Paket mit B und eins mit E dazwischenpassen */
 
-	struct mpqueue *ql, *q = dev->mp_last;
+	struct mpqueue *ql,
+	*q = dev->mp_last;
 	while (q) {
 		if (q->sqno < min_sqno) {
 			if (q->BEbyte & MP_END_FRAG) {
@@ -1368,8 +1644,8 @@
 					q->next->last = NULL;
 				while (q) {
 					ql = q->last;
-					q->skb->free = 1;
-					dev_kfree_skb(q->skb,0 /* FREE_READ */ );
+					SET_SKB_FREE(q->skb);
+					dev_kfree_skb(q->skb, 0 /* FREE_READ */ );
 					kfree(q);
 #ifdef CONFIG_ISDN_PPP_VJ
 					toss = 1;
@@ -1384,7 +1660,7 @@
 	}
 #ifdef CONFIG_ISDN_PPP_VJ
 	/* did we free a stale frame ? */
-	if(toss)
+	if (toss)
 		slhc_toss(ippp_table[dev->local.ppp_slot]->slcomp);
 #endif
 }
@@ -1395,26 +1671,28 @@
 
 #endif
 
-void isdn_ppp_timer_timeout(void)
+void
+isdn_ppp_timer_timeout(void)
 {
 #ifdef CONFIG_ISDN_MPP
 	isdn_net_dev *net_dev = dev->netdev;
-	struct sqqueue *q, *ql = NULL, *qn;
+	struct sqqueue *q,
+	*ql = NULL,
+	*qn;
 
 	while (net_dev) {
 		isdn_net_local *lp = &net_dev->local;
-		if (net_dev->ib.modify || lp->master)	{	/* interface locked or slave?*/
-                        net_dev = net_dev->next;
+		if (net_dev->ib.modify || lp->master) {	/* interface locked or slave? */
+			net_dev = net_dev->next;
 			continue;
-                }
-
+		}
 		q = net_dev->ib.sq;
 		while (q) {
 			if (q->sqno_start == net_dev->ib.next_num || q->timer < jiffies) {
 
 #ifdef CONFIG_ISDN_PPP_VJ
 				/* did we step over a missing frame ? */
-				if(q->sqno_start != net_dev->ib.next_num)
+				if (q->sqno_start != net_dev->ib.next_num)
 					slhc_toss(ippp_table[lp->ppp_slot]->slcomp);
 #endif
 
@@ -1441,47 +1719,57 @@
  * network device ioctl handlers
  */
 
-static int isdn_ppp_dev_ioctl_stats(int slot,struct ifreq *ifr,struct device *dev)
+static int
+isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct device *dev)
 {
-	struct ppp_stats *res, t;
+	struct ppp_stats *res,
+	 t;
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	int err;
 
 	res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+	err = verify_area(VERIFY_WRITE, res, sizeof(struct ppp_stats));
+
+	if (err)
+		return err;
 
 	/* build a temporary stat struct and copy it to user space */
 
-	memset (&t, 0, sizeof(struct ppp_stats));
-	if(dev->flags & IFF_UP) {
+	memset(&t, 0, sizeof(struct ppp_stats));
+	if (dev->flags & IFF_UP) {
 		t.p.ppp_ipackets = lp->stats.rx_packets;
 		t.p.ppp_ierrors = lp->stats.rx_errors;
 		t.p.ppp_opackets = lp->stats.tx_packets;
 		t.p.ppp_oerrors = lp->stats.tx_errors;
 #ifdef CONFIG_ISDN_PPP_VJ
-		if(slot >= 0 && ippp_table[slot]->slcomp) {
+		if (slot >= 0 && ippp_table[slot]->slcomp) {
 			struct slcompress *slcomp = ippp_table[slot]->slcomp;
-			t.vj.vjs_packets  = slcomp->sls_o_compressed+slcomp->sls_o_uncompressed;
+			t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed;
 			t.vj.vjs_compressed = slcomp->sls_o_compressed;
 			t.vj.vjs_searches = slcomp->sls_o_searches;
-			t.vj.vjs_misses   = slcomp->sls_o_misses;
-			t.vj.vjs_errorin  = slcomp->sls_i_error;
-			t.vj.vjs_tossed   = slcomp->sls_i_tossed;
+			t.vj.vjs_misses = slcomp->sls_o_misses;
+			t.vj.vjs_errorin = slcomp->sls_i_error;
+			t.vj.vjs_tossed = slcomp->sls_i_tossed;
 			t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
 			t.vj.vjs_compressedin = slcomp->sls_i_compressed;
 		}
 #endif
 	}
-	return copy_to_user (res, &t, sizeof (struct ppp_stats)) ? -EFAULT : 0;
+	copy_to_user(res, &t, sizeof(struct ppp_stats));
+	return 0;
+
 }
 
-int isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+int
+isdn_ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
 {
-	int error = 0;
+	int error;
 	char *r;
 	int len;
 	isdn_net_local *lp = (isdn_net_local *) dev->priv;
 
 #if 0
-	printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n",cmd,lp->ppp_slot);
+	printk(KERN_DEBUG "ippp, dev_ioctl: cmd %#08x , %d \n", cmd, lp->ppp_slot);
 #endif
 
 	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
@@ -1491,11 +1779,12 @@
 		case SIOCGPPPVER:
 			r = (char *) ifr->ifr_ifru.ifru_data;
 			len = strlen(PPP_VERSION) + 1;
-			if (copy_to_user(r, PPP_VERSION, len))
-				error = -EFAULT; 
+			error = verify_area(VERIFY_WRITE, r, len);
+			if (!error)
+				copy_to_user(r, PPP_VERSION, len);
 			break;
 		case SIOCGPPPSTATS:
-			error = isdn_ppp_dev_ioctl_stats (lp->ppp_slot, ifr, dev);
+			error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev);
 			break;
 		default:
 			error = -EINVAL;
@@ -1504,51 +1793,55 @@
 	return error;
 }
 
-static int isdn_ppp_if_get_unit(char *name)
+static int
+isdn_ppp_if_get_unit(char *name)
 {
-	int len, i, unit = 0, deci;
+	int len,
+	 i,
+	 unit = 0,
+	 deci;
 
 	len = strlen(name);
 
-	if(strncmp("ippp",name,4) || len > 8)
+	if (strncmp("ippp", name, 4) || len > 8)
 		return -1;
 
 	for (i = 0, deci = 1; i < len; i++, deci *= 10) {
-		char a = name[len-i-1];
+		char a = name[len - i - 1];
 		if (a >= '0' && a <= '9')
 			unit += (a - '0') * deci;
 		else
 			break;
 	}
-	if (!i || len-i != 4)
+	if (!i || len - i != 4)
 		unit = -1;
 
 	return unit;
 }
 
 
-int isdn_ppp_dial_slave(char *name)
+int
+isdn_ppp_dial_slave(char *name)
 {
 #ifdef CONFIG_ISDN_MPP
 	isdn_net_dev *ndev;
 	isdn_net_local *lp;
 	struct device *sdev;
 
-	if(!(ndev = isdn_net_findif(name)))
+	if (!(ndev = isdn_net_findif(name)))
 		return 1;
 	lp = &ndev->local;
-	if(!(lp->flags & ISDN_NET_CONNECTED))
+	if (!(lp->flags & ISDN_NET_CONNECTED))
 		return 5;
 
 	sdev = lp->slave;
-	while(sdev)
-	{
+	while (sdev) {
 		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
-		if(!(mlp->flags & ISDN_NET_CONNECTED))
+		if (!(mlp->flags & ISDN_NET_CONNECTED))
 			break;
 		sdev = mlp->slave;
 	}
-	if(!sdev)
+	if (!sdev)
 		return 2;
 
 	isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
@@ -1558,28 +1851,28 @@
 #endif
 }
 
-int isdn_ppp_hangup_slave(char *name)
+int
+isdn_ppp_hangup_slave(char *name)
 {
 #ifdef CONFIG_ISDN_MPP
-        isdn_net_dev *ndev;
-        isdn_net_local *lp;
-        struct device *sdev;
+	isdn_net_dev *ndev;
+	isdn_net_local *lp;
+	struct device *sdev;
 
-	if(!(ndev = isdn_net_findif(name)))
+	if (!(ndev = isdn_net_findif(name)))
 		return 1;
 	lp = &ndev->local;
-	if(!(lp->flags & ISDN_NET_CONNECTED))
+	if (!(lp->flags & ISDN_NET_CONNECTED))
 		return 5;
 
 	sdev = lp->slave;
-	while(sdev)
-	{
+	while (sdev) {
 		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
-		if((mlp->flags & ISDN_NET_CONNECTED))
+		if ((mlp->flags & ISDN_NET_CONNECTED))
 			break;
 		sdev = mlp->slave;
 	}
-	if(!sdev)
+	if (!sdev)
 		return 2;
 
 	isdn_net_hangup(sdev);
@@ -1589,3 +1882,12 @@
 #endif
 }
 
+#if 0
+static struct symbol_table isdn_ppp_syms =
+{
+#include <linux/symtab_begin.h>
+	X(isdn_ppp_register_compressor),
+	X(isdn_ppp_unregister_compressor),
+#include <linux/symtab_end.h>
+};
+#endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_ppp.h linux/drivers/isdn/isdn_ppp.h
--- v2.1.26/linux/drivers/isdn/isdn_ppp.h	Sat Aug 31 09:13:03 1996
+++ linux/drivers/isdn/isdn_ppp.h	Tue Feb 25 17:12:50 1997
@@ -1,9 +1,9 @@
-/* $Id: isdn_ppp.h,v 1.4 1996/05/06 11:34:56 hipp Exp $
- *
+/* $Id: isdn_ppp.h,v 1.9 1997/02/11 18:32:59 fritz Exp $
+
  * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
  *
  * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -16,9 +16,26 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_ppp.h,v $
+ * Revision 1.9  1997/02/11 18:32:59  fritz
+ * Bugfix in isdn_ppp_free_mpqueue().
+ *
+ * Revision 1.8  1997/02/10 10:11:33  fritz
+ * More changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.7  1997/02/03 23:18:57  fritz
+ * Removed isdn_ppp_free_sqqueue prototype
+ *         and ippp_table (both static in isdn_ppp.c).
+ *
+ * Revision 1.6  1996/09/23 01:58:11  fritz
+ * Fix: With syncPPP encapsulation, discard LCP packets
+ *      when calculating hangup timeout.
+ *
+ * Revision 1.5  1996/09/07 12:51:34  hipp
+ * *** empty log message ***
+ *
  * Revision 1.4  1996/05/06 11:34:56  hipp
  * fixed a few bugs
  *
@@ -34,23 +51,30 @@
  *
  */
 
+#include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
 extern void isdn_ppp_timer_timeout(void);
-extern int  isdn_ppp_read(int , struct file *, char *, int);
-extern int  isdn_ppp_write(int , struct file *, const char *, int);
-extern int  isdn_ppp_open(int , struct file *);
-extern int  isdn_ppp_init(void);
+extern int isdn_ppp_read(int, struct file *, char *, int);
+extern int isdn_ppp_write(int, struct file *, const char *, int);
+extern int isdn_ppp_open(int, struct file *);
+extern int isdn_ppp_init(void);
 extern void isdn_ppp_cleanup(void);
-extern int  isdn_ppp_free(isdn_net_local *);
-extern int  isdn_ppp_bind(isdn_net_local *);
-extern int  isdn_ppp_xmit(struct sk_buff *, struct device *);
+extern int isdn_ppp_free(isdn_net_local *);
+extern int isdn_ppp_bind(isdn_net_local *);
+extern int isdn_ppp_xmit(struct sk_buff *, struct device *);
 extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
-extern int  isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int);
-extern void isdn_ppp_free_mpqueue(isdn_net_dev *);
-extern void isdn_ppp_free_sqqueue(isdn_net_dev *);
-extern int  isdn_ppp_select(int, struct file *, int, select_table *);
-extern int  isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
+extern int isdn_ppp_dev_ioctl(struct device *, struct ifreq *, int);
+#if (LINUX_VERSION_CODE < 0x020117)
+extern int isdn_ppp_select(int, struct file *, int, select_table *);
+#else
+extern unsigned int isdn_ppp_poll(struct file *, poll_table *);
+#endif
+extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
 extern void isdn_ppp_release(int, struct file *);
-extern int  isdn_ppp_dial_slave(char *);
+extern int isdn_ppp_dial_slave(char *);
 extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
 
-extern struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+#define IPPP_OPEN	0x01
+#define IPPP_CONNECT	0x02
+#define IPPP_CLOSEWAIT	0x04
+#define IPPP_NOBLOCK	0x08
+#define IPPP_ASSIGNED	0x10
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_syms.c linux/drivers/isdn/isdn_syms.c
--- v2.1.26/linux/drivers/isdn/isdn_syms.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/isdn_syms.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,55 @@
+/* $Id: isdn_syms.c,v 1.3 1997/02/16 01:02:47 fritz Exp $
+
+ * Linux ISDN subsystem, exported symbols (linklevel).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn_syms.c,v $
+ * Revision 1.3  1997/02/16 01:02:47  fritz
+ * Added GPL-Header, Id and Log
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#ifndef __GENKSYMS__      /* Don't want genksyms report unneeded structs */
+#include <linux/isdn.h>
+#endif
+#include "isdn_common.h"
+
+#if (LINUX_VERSION_CODE < 0x020111)
+static int has_exported;
+
+static struct symbol_table isdn_syms = {
+#include <linux/symtab_begin.h>
+        X(register_isdn),
+#include <linux/symtab_end.h>
+};
+
+void
+isdn_export_syms(void)
+{
+	if (has_exported)
+		return;
+        register_symtab(&isdn_syms);
+        has_exported = 1;
+}
+
+#else
+
+EXPORT_SYMBOL(register_isdn);
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_tty.c linux/drivers/isdn/isdn_tty.c
--- v2.1.26/linux/drivers/isdn/isdn_tty.c	Sat Nov 30 00:48:34 1996
+++ linux/drivers/isdn/isdn_tty.c	Tue Feb 25 17:12:50 1997
@@ -1,10 +1,10 @@
-/* $Id: isdn_tty.c,v 1.21 1996/06/24 17:40:28 fritz Exp $
- *
+/* $Id: isdn_tty.c,v 1.29 1997/02/16 12:11:51 fritz Exp $
+
  * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -17,9 +17,37 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.c,v $
+ * Revision 1.29  1997/02/16 12:11:51  fritz
+ * Added S13,Bit4 option.
+ *
+ * Revision 1.28  1997/02/10 22:07:08  fritz
+ * Added 2 modem registers for numbering plan and screening info.
+ *
+ * Revision 1.27  1997/02/10 21:31:14  fritz
+ * Changed setup-interface (incoming and outgoing).
+ *
+ * Revision 1.26  1997/02/10 20:12:48  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.25  1997/02/03 23:04:30  fritz
+ * Reformatted according CodingStyle.
+ * skb->free stuff replaced by macro.
+ * Finished full-duplex audio.
+ *
+ * Revision 1.24  1997/01/14 01:32:42  fritz
+ * Changed audio receive not to rely on skb->users and skb->lock.
+ * Added ATI2 and related variables.
+ * Started adding full-duplex audio capability.
+ *
+ * Revision 1.23  1996/10/22 23:14:02  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
+ * Revision 1.22  1996/10/19 18:56:43  fritz
+ * ATZ did not change the xmitbuf size.
+ *
  * Revision 1.21  1996/06/24 17:40:28  fritz
  * Bugfix: Did not compile without CONFIG_ISDN_AUDIO
  *
@@ -109,7 +137,6 @@
  *
  */
 
-#include <asm/uaccess.h>
 #define __NO_VERSION__
 #include <linux/config.h>
 #include <linux/module.h>
@@ -124,9 +151,9 @@
 
 /* Prototypes */
 
-static int  isdn_tty_edit_at(const char *, int, modem_info *, int);
+static int isdn_tty_edit_at(const char *, int, modem_info *, int);
 static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
-static void isdn_tty_modem_reset_regs(atemu *, int);
+static void isdn_tty_modem_reset_regs(modem_info *, int);
 static void isdn_tty_cmd_ATA(modem_info *);
 static void isdn_tty_at_cout(char *, modem_info *);
 static void isdn_tty_flush_buffer(struct tty_struct *);
@@ -136,11 +163,13 @@
 #define MODEM_DO_RESTART
 
 static char *isdn_ttyname_ttyI = "ttyI";
-static char *isdn_ttyname_cui  = "cui";
-static int bit2si[8] = {1,5,7,7,7,7,7,7};
-static int si2bit[8] = {4,1,4,4,4,4,4,4};
-                                
-char *isdn_tty_revision        = "$Revision: 1.21 $";
+static char *isdn_ttyname_cui = "cui";
+static int bit2si[8] =
+{1, 5, 7, 7, 7, 7, 7, 7};
+static int si2bit[8] =
+{4, 1, 4, 4, 4, 4, 4, 4};
+
+char *isdn_tty_revision = "$Revision: 1.29 $";
 
 #define DLE 0x10
 #define ETX 0x03
@@ -155,37 +184,37 @@
  *  0 = Failure, data has to be buffered and later processed by
  *      isdn_tty_readmodem().
  */
-int isdn_tty_try_read(modem_info *info, struct sk_buff *skb)
+int
+isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
 {
-        int c;
-        int len;
-        struct tty_struct *tty;
+	int c;
+	int len;
+	struct tty_struct *tty;
 
 	if (info->online) {
 		if ((tty = info->tty)) {
 			if (info->mcr & UART_MCR_RTS) {
 				c = TTY_FLIPBUF_SIZE - tty->flip.count;
-                                len = skb->len + skb->users;
+				len = skb->len + ISDN_AUDIO_SKB_DLECOUNT(skb);
 				if (c >= len) {
-                                        if (skb->users)
-                                                while (skb->len--) {
-                                                        if (*skb->data == DLE)
-                                                                tty_insert_flip_char(tty, DLE, 0);
-                                                        tty_insert_flip_char(tty, *skb->data++, 0);
-                                                }
-                                        else {
-                                                memcpy(tty->flip.char_buf_ptr,
-                                                       skb->data, len);
-                                                tty->flip.count += len;
-                                                tty->flip.char_buf_ptr += len;
-                                                memset(tty->flip.flag_buf_ptr, 0, len);
-                                                tty->flip.flag_buf_ptr += len;
-                                        }
-                                        if (info->emu.mdmreg[12] & 128)
-                                                tty->flip.flag_buf_ptr[len - 1] = 0xff;
+					if (ISDN_AUDIO_SKB_DLECOUNT(skb))
+						while (skb->len--) {
+							if (*skb->data == DLE)
+								tty_insert_flip_char(tty, DLE, 0);
+							tty_insert_flip_char(tty, *skb->data++, 0);
+					} else {
+						memcpy(tty->flip.char_buf_ptr,
+						       skb->data, len);
+						tty->flip.count += len;
+						tty->flip.char_buf_ptr += len;
+						memset(tty->flip.flag_buf_ptr, 0, len);
+						tty->flip.flag_buf_ptr += len;
+					}
+					if (info->emu.mdmreg[12] & 128)
+						tty->flip.flag_buf_ptr[len - 1] = 0xff;
 					queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-                                        skb->free = 1;
-                                        kfree_skb(skb, FREE_READ);
+					SET_SKB_FREE(skb);
+					kfree_skb(skb, FREE_READ);
 					return 1;
 				}
 			}
@@ -198,7 +227,8 @@
  * It tries getting received data from the receive queue an stuff it into
  * the tty's flip-buffer.
  */
-void isdn_tty_readmodem(void)
+void
+isdn_tty_readmodem(void)
 {
 	int resched = 0;
 	int midx;
@@ -211,7 +241,7 @@
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		if ((midx = dev->m_idx[i]) >= 0) {
-                        info = &dev->mdm.info[midx];
+			info = &dev->mdm.info[midx];
 			if (info->online) {
 				r = 0;
 #ifdef CONFIG_ISDN_AUDIO
@@ -221,12 +251,12 @@
 					if (info->mcr & UART_MCR_RTS) {
 						c = TTY_FLIPBUF_SIZE - tty->flip.count;
 						if (c > 0) {
-                                                        save_flags(flags);
-                                                        cli();
+							save_flags(flags);
+							cli();
 							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
-								      tty->flip.char_buf_ptr,
-								      tty->flip.flag_buf_ptr, c, 0);
-                                                        /* CISCO AsyncPPP Hack */
+									   tty->flip.char_buf_ptr,
+									   tty->flip.flag_buf_ptr, c, 0);
+							/* CISCO AsyncPPP Hack */
 							if (!(info->emu.mdmreg[12] & 128))
 								memset(tty->flip.flag_buf_ptr, 0, r);
 							tty->flip.count += r;
@@ -234,7 +264,7 @@
 							tty->flip.char_buf_ptr += r;
 							if (r)
 								queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-                                                        restore_flags(flags);
+							restore_flags(flags);
 						}
 					} else
 						r = 1;
@@ -246,257 +276,265 @@
 				} else
 					info->rcvsched = 1;
 			}
-                }
+		}
 	}
 	if (!resched)
 		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
 }
 
-void isdn_tty_cleanup_xmit(modem_info *info)
+void
+isdn_tty_cleanup_xmit(modem_info * info)
 {
-        struct sk_buff *skb;
-        unsigned long flags;
+	struct sk_buff *skb;
+	unsigned long flags;
 
-        save_flags(flags);
-        cli();
-        if (skb_queue_len(&info->xmit_queue))
-                while ((skb = skb_dequeue(&info->xmit_queue))) {
-                        skb->free = 1;
-                        kfree_skb(skb, FREE_WRITE);
-                }
-        if (skb_queue_len(&info->dtmf_queue))
-                while ((skb = skb_dequeue(&info->dtmf_queue))) {
-                        skb->free = 1;
-                        kfree_skb(skb, FREE_WRITE);
-                }
-        restore_flags(flags);
-}
-
-static void isdn_tty_tint(modem_info *info)
-{
-        struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
-        int len, slen;
-
-        if (!skb)
-                return;
-        len = skb->len;
-        if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
-                                         info->isdn_channel, skb)) == len) {
-                struct tty_struct *tty = info->tty;
-                info->send_outstanding++;
-                info->msr |= UART_MSR_CTS;
-                info->lsr |= UART_LSR_TEMT;
-                if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-                    tty->ldisc.write_wakeup)
-                        (tty->ldisc.write_wakeup) (tty);
-                wake_up_interruptible(&tty->write_wait);
-                return;
-        }
-        if (slen > 0)
-                skb_pull(skb,slen);
-        skb_queue_head(&info->xmit_queue, skb);
+	save_flags(flags);
+	cli();
+	if (skb_queue_len(&info->xmit_queue))
+		while ((skb = skb_dequeue(&info->xmit_queue))) {
+			SET_SKB_FREE(skb);
+			kfree_skb(skb, FREE_WRITE);
+		}
+	if (skb_queue_len(&info->dtmf_queue))
+		while ((skb = skb_dequeue(&info->dtmf_queue))) {
+			SET_SKB_FREE(skb);
+			kfree_skb(skb, FREE_WRITE);
+		}
+	restore_flags(flags);
+}
+
+static void
+isdn_tty_tint(modem_info * info)
+{
+	struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
+	int len,
+	 slen;
+
+	if (!skb)
+		return;
+	len = skb->len;
+	if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
+				      info->isdn_channel, skb)) == len) {
+		struct tty_struct *tty = info->tty;
+		info->send_outstanding++;
+		info->msr |= UART_MSR_CTS;
+		info->lsr |= UART_LSR_TEMT;
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup) (tty);
+		wake_up_interruptible(&tty->write_wait);
+		return;
+	}
+	if (slen > 0)
+		skb_pull(skb, slen);
+	skb_queue_head(&info->xmit_queue, skb);
 }
 
 #ifdef CONFIG_ISDN_AUDIO
-int isdn_tty_countDLE(unsigned char *buf, int len)
+int
+isdn_tty_countDLE(unsigned char *buf, int len)
 {
-        int count = 0;
+	int count = 0;
 
-        while (len--)
-                if (*buf++ == DLE)
-                        count++;
-        return count;
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
 }
 
 /* This routine is called from within isdn_tty_write() to perform
  * DLE-decoding when sending audio-data.
  */
-static int isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len)
+static int
+isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len)
 {
-        unsigned char *p = &info->xmit_buf[info->xmit_count];
-        int count = 0;
+	unsigned char *p = &info->xmit_buf[info->xmit_count];
+	int count = 0;
 
-        while (len>0) {
-                if (m->lastDLE) {
-                        m->lastDLE = 0;
-                        switch (*p) {
-                                case DLE:
-                                        /* Escape code */
-                                        if (len>1)
-                                                memmove(p,p+1,len-1);
-                                        p--;
-                                        count++;
-                                        break;
-                                case ETX:
-                                        /* End of data */
-                                        info->vonline |= 4;
-                                        return count;
-                                case DC4:
-                                        /* Abort RX */
-                                        info->vonline &= ~1;
-                                        isdn_tty_at_cout("\020\003", info);
-                                        if (!info->vonline)
-                                                isdn_tty_at_cout("\r\nVCON\r\n", info);
-                                        /* Fall through */
-                                case 'q':
-                                case 's':
-                                        /* Silence */
-                                        if (len>1)
-                                                memmove(p,p+1,len-1);
-                                        p--;
-                                        break;
-                        }
-                } else {
-                        if (*p == DLE)
-                                m->lastDLE = 1;
-                        else
-                                count++;
-                }
-                p++;
-                len--;
-        }
-        if (len<0) {
-                printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
-                return 0;
-        }
-        return count;
+	while (len > 0) {
+		if (m->lastDLE) {
+			m->lastDLE = 0;
+			switch (*p) {
+				case DLE:
+					/* Escape code */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					count++;
+					break;
+				case ETX:
+					/* End of data */
+					info->vonline |= 4;
+					return count;
+				case DC4:
+					/* Abort RX */
+					info->vonline &= ~1;
+					isdn_tty_at_cout("\020\003", info);
+					if (!info->vonline)
+						isdn_tty_at_cout("\r\nVCON\r\n", info);
+					/* Fall through */
+				case 'q':
+				case 's':
+					/* Silence */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					break;
+			}
+		} else {
+			if (*p == DLE)
+				m->lastDLE = 1;
+			else
+				count++;
+		}
+		p++;
+		len--;
+	}
+	if (len < 0) {
+		printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
+		return 0;
+	}
+	return count;
 }
 
 /* This routine is called from within isdn_tty_write() when receiving
  * audio-data. It interrupts receiving, if an character other than
  * ^S or ^Q is sent.
  */
-static int isdn_tty_end_vrx(const char *buf, int c, int from_user)
+static int
+isdn_tty_end_vrx(const char *buf, int c, int from_user)
 {
 	char tmpbuf[VBUF];
-        char *p;
+	char *p;
 
-        if (c > VBUF) {
-                printk(KERN_ERR "isdn_tty: (end_vrx) BUFFER OVERFLOW!!!\n");
-                return 1;
-        }
+	if (c > VBUF) {
+		printk(KERN_ERR "isdn_tty: (end_vrx) BUFFER OVERFLOW!!!\n");
+		return 1;
+	}
 	if (from_user) {
 		copy_from_user(tmpbuf, buf, c);
-                p = tmpbuf;
-        } else
-                p = (char *)buf;
-        while (c--) {
-                if ((*p != 0x11) && (*p != 0x13))
-                        return 1;
-                p++;
-        }
-        return 0;
+		p = tmpbuf;
+	} else
+		p = (char *) buf;
+	while (c--) {
+		if ((*p != 0x11) && (*p != 0x13))
+			return 1;
+		p++;
+	}
+	return 0;
 }
 
-static int voice_cf[7] = { 1, 1, 4, 3, 2, 1, 1 };
+static int voice_cf[7] =
+{1, 1, 4, 3, 2, 1, 1};
 
-#endif        /* CONFIG_ISDN_AUDIO */
+#endif                          /* CONFIG_ISDN_AUDIO */
 
 /* isdn_tty_senddown() is called either directly from within isdn_tty_write()
  * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
  * outgoing data from the tty's xmit-buffer, handles voice-decompression or
  * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
  */
-static void isdn_tty_senddown(modem_info * info)
+static void
+isdn_tty_senddown(modem_info * info)
 {
-        unsigned char *buf = info->xmit_buf;
-        int buflen;
-        int skb_res;
-        struct sk_buff *skb;
-        unsigned long flags;
-
-        save_flags(flags);
-        cli();
-        if (!(buflen = info->xmit_count)) {
-                restore_flags(flags);
-                return;
-        }
-        if (info->isdn_driver < 0) {
-                info->xmit_count = 0;
-                restore_flags(flags);
-                return;
-        }
-        skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
+	unsigned char *buf = info->xmit_buf;
+	int buflen;
+	int skb_res;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (!(buflen = info->xmit_count)) {
+		restore_flags(flags);
+		return;
+	}
+	if (info->isdn_driver < 0) {
+		info->xmit_count = 0;
+		restore_flags(flags);
+		return;
+	}
+	skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
 #ifdef CONFIG_ISDN_AUDIO
-        if (info->vonline & 2) {
-                /* For now, ifmt is fixed to 1 (alaw), since this
-                 * is used with ISDN everywhere in the world, except
-                 * US, Canada and Japan.
-                 * Later, when US-ISDN protocols are implemented,
-                 * this setting will depend on the D-channel protocol.
-                 */
-                int ifmt = 1;
-                int skb_len;
-                unsigned char hbuf[VBUF];
-
-                memcpy(hbuf,info->xmit_buf,buflen);
-                info->xmit_count = 0;
-                restore_flags(flags);
-                /* voice conversion/decompression */
-                skb_len = buflen * voice_cf[info->emu.vpar[3]];
-                skb = dev_alloc_skb(skb_len + skb_res);
-                if (!skb) {
-                        printk(KERN_WARNING
-                               "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
-                        return;
-                }
-                skb_reserve(skb, skb_res);
-                switch (info->emu.vpar[3]) {
-                        case 2:
-                        case 3:
-                        case 4:
-                                /* adpcm, compatible to ZyXel 1496 modem
-                                 * with ROM revision 6.01
-                                 */
-                                buflen = isdn_audio_adpcm2xlaw(info->adpcms,
-                                                               ifmt,
-                                                               hbuf,
-                                                               skb_put(skb,skb_len),
-                                                               buflen);
-                                skb_trim(skb, buflen);
-                                break;
-                        case 5:
-                                /* a-law */
-                                if (!ifmt)
-                                        isdn_audio_alaw2ulaw(hbuf,buflen);
-                                memcpy(skb_put(skb,buflen),hbuf,buflen);
-                                break;
-                        case 6:
-                                /* u-law */
-                                if (ifmt)
-                                        isdn_audio_ulaw2alaw(hbuf,buflen);
-                                memcpy(skb_put(skb,buflen),hbuf,buflen);
-                                break;
-                }
-                if (info->vonline & 4) {
-                        info->vonline &= ~6;
-                        if (!info->vonline)
-                                isdn_tty_at_cout("\r\nVCON\r\n",info);
-                }
-        } else {
-#endif        /* CONFIG_ISDN_AUDIO */
-                skb = dev_alloc_skb(buflen + skb_res);
-                if (!skb) {
-                        printk(KERN_WARNING
-                               "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
-                        restore_flags(flags);
-                        return;
-                }
-                skb_reserve(skb, skb_res);
-                memcpy(skb_put(skb,buflen),buf,buflen);
-                info->xmit_count = 0;
-                restore_flags(flags);
+	if (info->vonline & 2) {
+		/* For now, ifmt is fixed to 1 (alaw), since this
+		 * is used with ISDN everywhere in the world, except
+		 * US, Canada and Japan.
+		 * Later, when US-ISDN protocols are implemented,
+		 * this setting will depend on the D-channel protocol.
+		 */
+		int ifmt = 1;
+		int skb_len;
+		unsigned char hbuf[VBUF];
+
+		memcpy(hbuf, info->xmit_buf, buflen);
+		info->xmit_count = 0;
+		restore_flags(flags);
+		/* voice conversion/decompression */
+		skb_len = buflen * voice_cf[info->emu.vpar[3]];
+		skb = dev_alloc_skb(skb_len + skb_res);
+		if (!skb) {
+			printk(KERN_WARNING
+			       "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
+			return;
+		}
+		skb_reserve(skb, skb_res);
+		switch (info->emu.vpar[3]) {
+			case 2:
+			case 3:
+			case 4:
+				/* adpcm, compatible to ZyXel 1496 modem
+				 * with ROM revision 6.01
+				 */
+				buflen = isdn_audio_adpcm2xlaw(info->adpcms,
+							       ifmt,
+							       hbuf,
+						   skb_put(skb, skb_len),
+							       buflen);
+				skb_trim(skb, buflen);
+				break;
+			case 5:
+				/* a-law */
+				if (!ifmt)
+					isdn_audio_alaw2ulaw(hbuf, buflen);
+				memcpy(skb_put(skb, buflen), hbuf, buflen);
+				break;
+			case 6:
+				/* u-law */
+				if (ifmt)
+					isdn_audio_ulaw2alaw(hbuf, buflen);
+				memcpy(skb_put(skb, buflen), hbuf, buflen);
+				break;
+		}
+		if (info->vonline & 4) {
+			info->vonline &= ~6;
+			if (!info->vonline)
+				isdn_tty_at_cout("\r\nVCON\r\n", info);
+		}
+	} else {
+#endif                          /* CONFIG_ISDN_AUDIO */
+		skb = dev_alloc_skb(buflen + skb_res);
+		if (!skb) {
+			printk(KERN_WARNING
+			       "isdn_tty: Out of memory in ttyI%d senddown\n", info->line);
+			restore_flags(flags);
+			return;
+		}
+		skb_reserve(skb, skb_res);
+		memcpy(skb_put(skb, buflen), buf, buflen);
+		info->xmit_count = 0;
+		restore_flags(flags);
 #ifdef CONFIG_ISDN_AUDIO
-        }
+	}
 #endif
-        skb->free = 1;
-        if (info->emu.mdmreg[13] & 2)
-                /* Add T.70 simplified header */
-                memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
-        skb_queue_tail(&info->xmit_queue, skb);
-        if ((info->emu.mdmreg[12] & 0x10) != 0)
-                info->msr &= UART_MSR_CTS;
-        info->lsr &= UART_LSR_TEMT;
+	SET_SKB_FREE(skb);
+	if (info->emu.mdmreg[13] & 2)
+		/* Add T.70 simplified header */
+		memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+	skb_queue_tail(&info->xmit_queue, skb);
+	if ((info->emu.mdmreg[12] & 0x10) != 0)
+		info->msr &= UART_MSR_CTS;
+	info->lsr &= UART_LSR_TEMT;
 }
 
 /************************************************************
@@ -512,53 +550,56 @@
  * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
  * into the tty's flip-buffer.
  */
-static void isdn_tty_modem_do_ncarrier(unsigned long data)
+static void
+isdn_tty_modem_do_ncarrier(unsigned long data)
 {
-        modem_info * info = (modem_info *)data;
-        isdn_tty_modem_result(3, info);
+	modem_info *info = (modem_info *) data;
+	isdn_tty_modem_result(3, info);
 }
 
 /* Next routine is called, whenever the DTR-signal is raised.
  * It checks the ncarrier-flag, and triggers the above routine
  * when necessary. The ncarrier-flag is set, whenever DTR goes
  * low.
- */      
-static void isdn_tty_modem_ncarrier(modem_info * info)
+ */
+static void
+isdn_tty_modem_ncarrier(modem_info * info)
 {
-        if (info->ncarrier) {
-                info->ncarrier = 0;
-                info->nc_timer.expires = jiffies + HZ;
-                info->nc_timer.function = isdn_tty_modem_do_ncarrier;
-                info->nc_timer.data = (unsigned long)info;
-                add_timer(&info->nc_timer);
-        }
+	if (info->ncarrier) {
+		info->ncarrier = 0;
+		info->nc_timer.expires = jiffies + HZ;
+		info->nc_timer.function = isdn_tty_modem_do_ncarrier;
+		info->nc_timer.data = (unsigned long) info;
+		add_timer(&info->nc_timer);
+	}
 }
 
 /* isdn_tty_dial() performs dialing of a tty an the necessary
  * setup of the lower levels before that.
  */
-static void isdn_tty_dial(char *n, modem_info * info, atemu * m)
+static void
+isdn_tty_dial(char *n, modem_info * info, atemu * m)
 {
-        int usg = ISDN_USAGE_MODEM;
-        int si = 7;
-        int l2 = m->mdmreg[14];
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[14];
 	isdn_ctrl cmd;
 	ulong flags;
 	int i;
-        int j;
+	int j;
 
-        for (j=7;j>=0;j--)
-                if (m->mdmreg[18] & (1<<j)) {
-                        si = bit2si[j];
-                        break;
-                }
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[18] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
 #ifdef CONFIG_ISDN_AUDIO
-                if (si == 1) {
-                        l2 = 4;
-                        usg = ISDN_USAGE_VOICE;
-                }
+	if (si == 1) {
+		l2 = 4;
+		usg = ISDN_USAGE_VOICE;
+	}
 #endif
-        m->mdmreg[20] = si2bit[si];
+	m->mdmreg[20] = si2bit[si];
 	save_flags(flags);
 	cli();
 	i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1);
@@ -566,38 +607,44 @@
 		restore_flags(flags);
 		isdn_tty_modem_result(6, info);
 	} else {
-                info->isdn_driver = dev->drvmap[i];
-                info->isdn_channel = dev->chanmap[i];
-                info->drv_index = i;
-                dev->m_idx[i] = info->line;
-                dev->usage[i] |= ISDN_USAGE_OUTGOING;
-                isdn_info_update();
-                restore_flags(flags);
-                cmd.driver = info->isdn_driver;
-                cmd.arg = info->isdn_channel;
-                cmd.command = ISDN_CMD_CLREAZ;
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                strcpy(cmd.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETEAZ;
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL2;
-                cmd.arg = info->isdn_channel + (l2 << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL3;
-                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.arg = info->isdn_channel;
-                sprintf(cmd.num, "%s,%s,%d,%d", n, isdn_map_eaz2msn(m->msn, info->isdn_driver),
-                        si, m->mdmreg[19]);
-                cmd.command = ISDN_CMD_DIAL;
-                info->dialing = 1;
-                strcpy(dev->num[i], n);
-                isdn_info_update();
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		strcpy(info->last_num, n);
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		sprintf(cmd.parm.setup.phone, "%s", n);
+		sprintf(cmd.parm.setup.eazmsn, "%s",
+			isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.parm.setup.si1 = si;
+		cmd.parm.setup.si2 = m->mdmreg[19];
+		cmd.command = ISDN_CMD_DIAL;
+		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
 	}
 }
 
@@ -605,64 +652,69 @@
  * ISDN-line (hangup). The usage-status is cleared
  * and some cleanup is done also.
  */
-void isdn_tty_modem_hup(modem_info * info)
+void
+isdn_tty_modem_hup(modem_info * info, int local)
 {
 	isdn_ctrl cmd;
-        int usage;
+	int usage;
 
-        if (!info)
-                return;
+	if (!info)
+		return;
 #ifdef ISDN_DEBUG_MODEM_HUP
-        printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
+	printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
 #endif
-        info->rcvsched = 0;
-        info->online = 0;
-        isdn_tty_flush_buffer(info->tty);
-        if (info->vonline & 1) {
-                /* voice-recording, add DLE-ETX */
-                isdn_tty_at_cout("\020\003", info);
-        }
-        if (info->vonline & 2) {
-                /* voice-playing, add DLE-DC4 */
-                isdn_tty_at_cout("\020\024", info);
-        }
-        info->vonline = 0;
+	info->rcvsched = 0;
+	info->online = 0;
+	if (info->online || info->vonline)
+		info->last_lhup = local;
+	isdn_tty_flush_buffer(info->tty);
+	if (info->vonline & 1) {
+		/* voice-recording, add DLE-ETX */
+		isdn_tty_at_cout("\020\003", info);
+	}
+	if (info->vonline & 2) {
+		/* voice-playing, add DLE-DC4 */
+		isdn_tty_at_cout("\020\024", info);
+	}
+	info->vonline = 0;
 #ifdef CONFIG_ISDN_AUDIO
-        if (info->dtmf_state) {
-                kfree(info->dtmf_state);
-                info->dtmf_state = NULL;
-        }
-        if (info->adpcms) {
-                kfree(info->adpcms);
-                info->adpcms = NULL;
-        }
-        if (info->adpcmr) {
-                kfree(info->adpcmr);
-                info->adpcmr = NULL;
-        }
+	if (info->dtmf_state) {
+		kfree(info->dtmf_state);
+		info->dtmf_state = NULL;
+	}
+	if (info->adpcms) {
+		kfree(info->adpcms);
+		info->adpcms = NULL;
+	}
+	if (info->adpcmr) {
+		kfree(info->adpcmr);
+		info->adpcmr = NULL;
+	}
 #endif
-        info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
-        info->lsr |= UART_LSR_TEMT;
+	info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+	info->lsr |= UART_LSR_TEMT;
 	if (info->isdn_driver >= 0) {
 		cmd.driver = info->isdn_driver;
 		cmd.command = ISDN_CMD_HANGUP;
 		cmd.arg = info->isdn_channel;
 		dev->drv[info->isdn_driver]->interface->command(&cmd);
 		isdn_all_eaz(info->isdn_driver, info->isdn_channel);
-                usage = (info->emu.mdmreg[20] == 1)?
-                        ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
+		info->emu.mdmreg[1] = 0;
+		usage = (info->emu.mdmreg[20] == 1) ?
+		    ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
 		isdn_free_channel(info->isdn_driver, info->isdn_channel,
-                                  usage);
+				  usage);
 	}
 	info->isdn_driver = -1;
 	info->isdn_channel = -1;
-        if (info->drv_index >= 0) {
-                dev->m_idx[info->drv_index] = -1;
-                info->drv_index = -1;
-        }
+	if (info->drv_index >= 0) {
+		dev->m_idx[info->drv_index] = -1;
+		info->drv_index = -1;
+	}
 }
 
-static inline int isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
+static inline int
+isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
 {
 #ifdef MODEM_PARANOIA_CHECK
 	if (!info) {
@@ -683,9 +735,13 @@
  * This routine is called to set the UART divisor registers to match
  * the specified baud rate for a serial port.
  */
-static void isdn_tty_change_speed(modem_info * info)
+static void
+isdn_tty_change_speed(modem_info * info)
 {
-	uint cflag, cval, fcr, quot;
+	uint cflag,
+	 cval,
+	 fcr,
+	 quot;
 	int i;
 
 	if (!info->tty || !info->tty->termios)
@@ -702,19 +758,19 @@
 	}
 	if (quot) {
 		info->mcr |= UART_MCR_DTR;
-                isdn_tty_modem_ncarrier(info);                
+		isdn_tty_modem_ncarrier(info);
 	} else {
 		info->mcr &= ~UART_MCR_DTR;
-                if (info->emu.mdmreg[13] & 4) {
+		if (info->emu.mdmreg[13] & 4) {
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "Mhup in changespeed\n");
+			printk(KERN_DEBUG "Mhup in changespeed\n");
 #endif
-                        if (info->online)
-                                info->ncarrier = 1;
-                        isdn_tty_modem_reset_regs(&info->emu, 0);
-                        isdn_tty_modem_hup(info);
-                }
-                return;
+			if (info->online)
+				info->ncarrier = 1;
+			isdn_tty_modem_reset_regs(info, 0);
+			isdn_tty_modem_hup(info, 1);
+		}
+		return;
 	}
 	/* byte size and parity */
 	cval = cflag & (CSIZE | CSTOPB);
@@ -737,7 +793,8 @@
 	}
 }
 
-static int isdn_tty_startup(modem_info * info)
+static int
+isdn_tty_startup(modem_info * info)
 {
 	ulong flags;
 
@@ -745,12 +802,12 @@
 		return 0;
 	save_flags(flags);
 	cli();
-        isdn_MOD_INC_USE_COUNT();
+	isdn_MOD_INC_USE_COUNT();
 #ifdef ISDN_DEBUG_MODEM_OPEN
 	printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
 #endif
 	/*
-	 * Now, initialize the UART 
+	 * Now, initialize the UART
 	 */
 	info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
 	if (info->tty)
@@ -771,7 +828,8 @@
  * This routine will shutdown a serial port; interrupts are disabled, and
  * DTR is dropped if the hangup on close termio flag is on.
  */
-static void isdn_tty_shutdown(modem_info * info)
+static void
+isdn_tty_shutdown(modem_info * info)
 {
 	ulong flags;
 
@@ -781,17 +839,18 @@
 	printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
 #endif
 	save_flags(flags);
-	cli();			/* Disable interrupts */
-        isdn_MOD_DEC_USE_COUNT();
+	cli();                  /* Disable interrupts */
+	isdn_MOD_DEC_USE_COUNT();
+	info->msr &= ~UART_MSR_RI;
 	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
 		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
-                if (info->emu.mdmreg[13] & 4) {
-                        isdn_tty_modem_reset_regs(&info->emu, 0);
+		if (info->emu.mdmreg[13] & 4) {
+			isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+			printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
 #endif
-                        isdn_tty_modem_hup(info);
-                }
+			isdn_tty_modem_hup(info, 1);
+		}
 	}
 	if (info->tty)
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -809,9 +868,11 @@
  *  - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
  *  - If dialing, abort dial.
  */
-static int isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
+static int
+isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
 {
-	int c, total = 0;
+	int c,
+	 total = 0;
 	ulong flags;
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -819,70 +880,73 @@
 		return 0;
 	if (!tty)
 		return 0;
-        save_flags(flags);
-        cli();
+	save_flags(flags);
+	cli();
 	while (1) {
 		c = MIN(count, info->xmit_size - info->xmit_count);
 		if (info->isdn_driver >= 0)
 			c = MIN(c, dev->drv[info->isdn_driver]->maxbufsize);
 		if (c <= 0)
 			break;
-                if ((info->online > 1) ||
-                    (info->vonline & 2)) {
-                        atemu *m = &info->emu;
-
-                        if (!(info->vonline & 2))
-                                isdn_tty_check_esc(buf, m->mdmreg[2], c,
-                                                   &(m->pluscount),
-                                                   &(m->lastplus),
-                                                   from_user);
-                        if (from_user)
-                                copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
-                        else
-                                memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+		if ((info->online > 1) ||
+		    (info->vonline & 2)) {
+			atemu *m = &info->emu;
+
+			if (!(info->vonline & 2))
+				isdn_tty_check_esc(buf, m->mdmreg[2], c,
+						   &(m->pluscount),
+						   &(m->lastplus),
+						   from_user);
+			if (from_user)
+				copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
+			else
+				memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
 #ifdef CONFIG_ISDN_AUDIO
-                        if (info->vonline & 2) {
-                                int cc;
+			if (info->vonline & 2) {
+				int cc;
 
-                                if (!(cc = isdn_tty_handleDLEdown(info,m,c))) {
-                                        /* If DLE decoding results in zero-transmit, but
-                                         * c originally was non-zero, do a wakeup.
-                                         */
-                                        if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-                                            tty->ldisc.write_wakeup)
-                                                (tty->ldisc.write_wakeup) (tty);
-                                        wake_up_interruptible(&tty->write_wait);
-                                        info->msr |= UART_MSR_CTS;
-                                        info->lsr |= UART_LSR_TEMT;
-                                }
-                                info->xmit_count += cc;
-                        } else
+				if (!(cc = isdn_tty_handleDLEdown(info, m, c))) {
+					/* If DLE decoding results in zero-transmit, but
+					 * c originally was non-zero, do a wakeup.
+					 */
+					if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+					    tty->ldisc.write_wakeup)
+						(tty->ldisc.write_wakeup) (tty);
+					wake_up_interruptible(&tty->write_wait);
+					info->msr |= UART_MSR_CTS;
+					info->lsr |= UART_LSR_TEMT;
+				}
+				info->xmit_count += cc;
+			} else
 #endif
-                                info->xmit_count += c;
+				info->xmit_count += c;
 			if (m->mdmreg[13] & 1) {
-                                isdn_tty_senddown(info);
-                                isdn_tty_tint(info);
-                        }
+				isdn_tty_senddown(info);
+				isdn_tty_tint(info);
+			}
 		} else {
-                        info->msr |= UART_MSR_CTS;
-                        info->lsr |= UART_LSR_TEMT;
+			info->msr |= UART_MSR_CTS;
+			info->lsr |= UART_LSR_TEMT;
 #ifdef CONFIG_ISDN_AUDIO
-                        if (info->vonline & 1) {
-                                if (isdn_tty_end_vrx(buf, c, from_user)) {
-                                        info->vonline &= ~1;
-                                        isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
-                                }
-                        } else
+			if ((info->vonline & 3) == 1) {
+				/* Do NOT handle Ctrl-Q or Ctrl-S
+				 * when in full-duplex audio mode.
+				 */
+				if (isdn_tty_end_vrx(buf, c, from_user)) {
+					info->vonline &= ~1;
+					isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
+				}
+			} else
 #endif
-                                if (info->dialing) {
-                                        info->dialing = 0;
+			if (info->dialing) {
+				info->dialing = 0;
 #ifdef ISDN_DEBUG_MODEM_HUP
-                                        printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+				printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
 #endif
-                                        isdn_tty_modem_result(3, info);
-                                        isdn_tty_modem_hup(info);
-                                } else
-                                        c = isdn_tty_edit_at(buf, c, info, from_user);
+				isdn_tty_modem_result(3, info);
+				isdn_tty_modem_hup(info, 1);
+			} else
+				c = isdn_tty_edit_at(buf, c, info, from_user);
 		}
 		buf += c;
 		count -= c;
@@ -890,11 +954,12 @@
 	}
 	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
 		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
-        restore_flags(flags);
+	restore_flags(flags);
 	return total;
 }
 
-static int isdn_tty_write_room(struct tty_struct *tty)
+static int
+isdn_tty_write_room(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 	int ret;
@@ -907,7 +972,8 @@
 	return (ret < 0) ? 0 : ret;
 }
 
-static int isdn_tty_chars_in_buffer(struct tty_struct *tty)
+static int
+isdn_tty_chars_in_buffer(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -918,32 +984,34 @@
 	return (info->xmit_count);
 }
 
-static void isdn_tty_flush_buffer(struct tty_struct *tty)
+static void
+isdn_tty_flush_buffer(struct tty_struct *tty)
 {
 	modem_info *info;
-        unsigned long flags;
+	unsigned long flags;
 
-        save_flags(flags);
-        cli();
-        if (!tty) {
-                restore_flags(flags);
-                return;
-        }
-        info =  (modem_info *) tty->driver_data;
+	save_flags(flags);
+	cli();
+	if (!tty) {
+		restore_flags(flags);
+		return;
+	}
+	info = (modem_info *) tty->driver_data;
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) {
-                restore_flags(flags);
+		restore_flags(flags);
 		return;
-        }
-        isdn_tty_cleanup_xmit(info);
-        info->xmit_count = 0;
-        restore_flags(flags);
+	}
+	isdn_tty_cleanup_xmit(info);
+	info->xmit_count = 0;
+	restore_flags(flags);
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup) (tty);
 }
 
-static void isdn_tty_flush_chars(struct tty_struct *tty)
+static void
+isdn_tty_flush_chars(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -956,12 +1024,13 @@
 /*
  * ------------------------------------------------------------
  * isdn_tty_throttle()
- * 
+ *
  * This routine is called by the upper-layer tty layer to signal that
  * incoming characters should be throttled.
  * ------------------------------------------------------------
  */
-static void isdn_tty_throttle(struct tty_struct *tty)
+static void
+isdn_tty_throttle(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -972,7 +1041,8 @@
 	info->mcr &= ~UART_MCR_RTS;
 }
 
-static void isdn_tty_unthrottle(struct tty_struct *tty)
+static void
+isdn_tty_unthrottle(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -1001,9 +1071,10 @@
  *          release the bus after transmitting. This must be done when
  *          the transmit shift register is empty, not be done when the
  *          transmit holding register is empty.  This functionality
- *          allows RS485 driver to be written in user space. 
+ *          allows RS485 driver to be written in user space.
  */
-static int isdn_tty_get_lsr_info(modem_info * info, uint * value)
+static int
+isdn_tty_get_lsr_info(modem_info * info, uint * value)
 {
 	u_char status;
 	uint result;
@@ -1014,13 +1085,16 @@
 	status = info->lsr;
 	restore_flags(flags);
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-	return put_user(result, value);
+	put_user(result, (ulong *) value);
+	return 0;
 }
 
 
-static int isdn_tty_get_modem_info(modem_info * info, uint * value)
+static int
+isdn_tty_get_modem_info(modem_info * info, uint * value)
 {
-	u_char control, status;
+	u_char control,
+	 status;
 	uint result;
 	ulong flags;
 
@@ -1035,83 +1109,82 @@
 	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
 	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
 	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-	return put_user(result, value);
+	put_user(result, (ulong *) value);
+	return 0;
 }
 
-static int isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
+static int
+isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
 {
 	uint arg;
-    int pre_dtr;
-	int error;
-
-	error = get_user(arg, ((uint *) value));
-	if (error)
-	        return error;
+	int pre_dtr;
 
+	GET_USER(arg, (uint *) value);
 	switch (cmd) {
-                case TIOCMBIS:
+		case TIOCMBIS:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
 #endif
-                        if (arg & TIOCM_RTS) {
-                                info->mcr |= UART_MCR_RTS;
-                        }
-                        if (arg & TIOCM_DTR) {
-                                info->mcr |= UART_MCR_DTR;
-                                isdn_tty_modem_ncarrier(info);
-                        }
-                        break;
-                case TIOCMBIC:
+			if (arg & TIOCM_RTS) {
+				info->mcr |= UART_MCR_RTS;
+			}
+			if (arg & TIOCM_DTR) {
+				info->mcr |= UART_MCR_DTR;
+				isdn_tty_modem_ncarrier(info);
+			}
+			break;
+		case TIOCMBIC:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
 #endif
-                        if (arg & TIOCM_RTS) {
-                                info->mcr &= ~UART_MCR_RTS;
-                        }
-                        if (arg & TIOCM_DTR) {
-                                info->mcr &= ~UART_MCR_DTR;
-                                if (info->emu.mdmreg[13] & 4) {
-                                        isdn_tty_modem_reset_regs(&info->emu, 0);
+			if (arg & TIOCM_RTS) {
+				info->mcr &= ~UART_MCR_RTS;
+			}
+			if (arg & TIOCM_DTR) {
+				info->mcr &= ~UART_MCR_DTR;
+				if (info->emu.mdmreg[13] & 4) {
+					isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                                        printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
+					printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
 #endif
-                                        if (info->online)
-                                                info->ncarrier = 1;
-                                        isdn_tty_modem_hup(info);
-                                }
-                        }
-                        break;
-                case TIOCMSET:
+					if (info->online)
+						info->ncarrier = 1;
+					isdn_tty_modem_hup(info, 1);
+				}
+			}
+			break;
+		case TIOCMSET:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
 #endif
-                        pre_dtr = (info->mcr & UART_MCR_DTR);
-                        info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
-                                     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
-                                     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
-                        if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
-                                if (!(info->mcr & UART_MCR_DTR)) {
-                                        if (info->emu.mdmreg[13] & 4) {
-                                                isdn_tty_modem_reset_regs(&info->emu, 0);
+			pre_dtr = (info->mcr & UART_MCR_DTR);
+			info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
+				 | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+			       | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+			if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
+				if (!(info->mcr & UART_MCR_DTR)) {
+					if (info->emu.mdmreg[13] & 4) {
+						isdn_tty_modem_reset_regs(info, 0);
 #ifdef ISDN_DEBUG_MODEM_HUP
-                                                printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+						printk(KERN_DEBUG "Mhup in TIOCMSET\n");
 #endif
-                                                if (info->online)
-                                                        info->ncarrier = 1;
-                                                isdn_tty_modem_hup(info);
-                                        }
-                                } else
-                                        isdn_tty_modem_ncarrier(info);
-                        }
-                        break;
-                default:
-                        return -EINVAL;
+						if (info->online)
+							info->ncarrier = 1;
+						isdn_tty_modem_hup(info, 1);
+					}
+				} else
+					isdn_tty_modem_ncarrier(info);
+			}
+			break;
+		default:
+			return -EINVAL;
 	}
 	return 0;
 }
 
-static int isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
-		       uint cmd, ulong arg)
+static int
+isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+	       uint cmd, ulong arg)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 	int error;
@@ -1119,82 +1192,98 @@
 
 	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
 		return -ENODEV;
-        if (tty->flags & (1 << TTY_IO_ERROR))
-                return -EIO;
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
 	switch (cmd) {
-                case TCSBRK:		/* SVID version: non-zero arg --> no break */
+		case TCSBRK:   /* SVID version: non-zero arg --> no break */
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
 #endif
-                        retval = tty_check_change(tty);
-                        if (retval)
-                                return retval;
-                        tty_wait_until_sent(tty, 0);
-                        return 0;
-                case TCSBRKP:		/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TCSBRKP:  /* support for POSIX tcsendbreak() */
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
 #endif
-                        retval = tty_check_change(tty);
-                        if (retval)
-                                return retval;
-                        tty_wait_until_sent(tty, 0);
-                        return 0;
-                case TIOCGSOFTCAR:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TIOCGSOFTCAR:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
 #endif
-			return put_user(C_CLOCAL(tty) ? 1 : 0, (uint *) arg);
-                case TIOCSSOFTCAR:
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+			if (error)
+				return error;
+			put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
+			return 0;
+		case TIOCSSOFTCAR:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
 #endif
-                        error = get_user(arg ,((uint *) arg));
+			error = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
 			if (error)
 				return error;
-                        tty->termios->c_cflag =
-                                ((tty->termios->c_cflag & ~CLOCAL) |
-                                 (arg ? CLOCAL : 0));
-                        return 0;
-                case TIOCMGET:
+			GET_USER(arg, (ulong *) arg);
+			tty->termios->c_cflag =
+			    ((tty->termios->c_cflag & ~CLOCAL) |
+			     (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
 #endif
-                        return isdn_tty_get_modem_info(info, (uint *) arg);
-                case TIOCMBIS:
-                case TIOCMBIC:
-                case TIOCMSET:
-                        return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
-                case TIOCSERGETLSR:	/* Get line status register */
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+			if (error)
+				return error;
+			return isdn_tty_get_modem_info(info, (uint *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint));
+			if (error)
+				return error;
+			return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
+		case TIOCSERGETLSR:	/* Get line status register */
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
 #endif
-                        return isdn_tty_get_lsr_info(info, (uint *) arg);
-                        
-                default:
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
+			if (error)
+				return error;
+			else
+				return isdn_tty_get_lsr_info(info, (uint *) arg);
+
+		default:
 #ifdef ISDN_DEBUG_MODEM_IOCTL
-                        printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+			printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
 #endif
-                        return -ENOIOCTLCMD;
+			return -ENOIOCTLCMD;
 	}
 	return 0;
 }
 
-static void isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+static void
+isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
-        if (!old_termios)
-                isdn_tty_change_speed(info);
-        else {
-                if (tty->termios->c_cflag == old_termios->c_cflag)
-                        return;
-                isdn_tty_change_speed(info);
-                if ((old_termios->c_cflag & CRTSCTS) &&
-                    !(tty->termios->c_cflag & CRTSCTS)) {
-                        tty->hw_stopped = 0;
-                }
-        }
+	if (!old_termios)
+		isdn_tty_change_speed(info);
+	else {
+		if (tty->termios->c_cflag == old_termios->c_cflag)
+			return;
+		isdn_tty_change_speed(info);
+		if ((old_termios->c_cflag & CRTSCTS) &&
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			tty->hw_stopped = 0;
+		}
+	}
 }
 
 /*
@@ -1202,9 +1291,11 @@
  * isdn_tty_open() and friends
  * ------------------------------------------------------------
  */
-static int isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+static int
+isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
 {
-	struct wait_queue wait = {current, NULL};
+	struct wait_queue wait =
+	{current, NULL};
 	int do_clocal = 0;
 	unsigned long flags;
 	int retval;
@@ -1215,8 +1306,8 @@
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ISDN_ASYNC_CLOSING)) {
-                if (info->flags & ISDN_ASYNC_CLOSING)
-                        interruptible_sleep_on(&info->close_wait);
+		if (info->flags & ISDN_ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
 #ifdef MODEM_DO_RESTART
 		if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
 			return -EAGAIN;
@@ -1249,7 +1340,7 @@
 	 * and then exit.
 	 */
 	if ((filp->f_flags & O_NONBLOCK) ||
-            (tty->flags & (1 << TTY_IO_ERROR))) {
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
 		if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
 			return -EBUSY;
 		info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
@@ -1275,11 +1366,11 @@
 	printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
 	       info->line, info->count);
 #endif
-        save_flags(flags);
-        cli();
-        if (!(tty_hung_up_p(filp)))
-                info->count--;
-        restore_flags(flags);
+	save_flags(flags);
+	cli();
+	if (!(tty_hung_up_p(filp)))
+		info->count--;
+	restore_flags(flags);
 	info->blocked_open++;
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
@@ -1331,10 +1422,12 @@
  * the IRQ chain.   It also performs the serial-specific
  * initialization for the tty structure.
  */
-static int isdn_tty_open(struct tty_struct *tty, struct file *filp)
+static int
+isdn_tty_open(struct tty_struct *tty, struct file *filp)
 {
 	modem_info *info;
-	int retval, line;
+	int retval,
+	 line;
 
 	line = MINOR(tty->device) - tty->driver.minor_start;
 	if (line < 0 || line > ISDN_MAX_CHANNELS)
@@ -1385,7 +1478,8 @@
 	return 0;
 }
 
-static void isdn_tty_close(struct tty_struct *tty, struct file *filp)
+static void
+isdn_tty_close(struct tty_struct *tty, struct file *filp)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 	ulong flags;
@@ -1436,7 +1530,7 @@
 	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
 		info->callout_termios = *tty->termios;
 
-        tty->closing = 1;
+	tty->closing = 1;
 	/*
 	 * At this point we stop accepting input.  To do this, we
 	 * disable the receive line status interrupts, and tell the
@@ -1444,7 +1538,7 @@
 	 * line status register.
 	 */
 	if (info->flags & ISDN_ASYNC_INITIALIZED) {
-		tty_wait_until_sent(tty, 3000);		/* 30 seconds timeout */
+		tty_wait_until_sent(tty, 3000);	/* 30 seconds timeout */
 		/*
 		 * Before we drop DTR, make sure the UART transmitter
 		 * has completely drained; this is especially
@@ -1466,12 +1560,12 @@
 	if (tty->ldisc.flush_buffer)
 		tty->ldisc.flush_buffer(tty);
 	info->tty = 0;
-        info->ncarrier = 0;
+	info->ncarrier = 0;
 	tty->closing = 0;
 	if (info->blocked_open) {
-                current->state = TASK_INTERRUPTIBLE;
-                current->timeout = jiffies + 50;
-                schedule();
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 50;
+		schedule();
 		wake_up_interruptible(&info->open_wait);
 	}
 	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
@@ -1486,7 +1580,8 @@
 /*
  * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
  */
-static void isdn_tty_hangup(struct tty_struct *tty)
+static void
+isdn_tty_hangup(struct tty_struct *tty)
 {
 	modem_info *info = (modem_info *) tty->driver_data;
 
@@ -1501,7 +1596,8 @@
 
 /* This routine initializes all emulator-data.
  */
-static void isdn_tty_reset_profile(atemu * m)
+static void
+isdn_tty_reset_profile(atemu * m)
 {
 	m->profile[0] = 0;
 	m->profile[1] = 0;
@@ -1527,25 +1623,30 @@
 	m->pmsn[0] = '\0';
 }
 
-static void isdn_tty_modem_reset_vpar(atemu *m)
+static void
+isdn_tty_modem_reset_vpar(atemu * m)
 {
-        m->vpar[0] = 2;  /* Voice-device            (2 = phone line) */
-        m->vpar[1] = 0;  /* Silence detection level (0 = none      ) */
-        m->vpar[2] = 70; /* Silence interval        (7 sec.        ) */
-        m->vpar[3] = 2;  /* Compression type        (1 = ADPCM-2   ) */
+	m->vpar[0] = 2;         /* Voice-device            (2 = phone line) */
+	m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
+	m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
+	m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
 }
 
-static void isdn_tty_modem_reset_regs(atemu * m, int force)
+static void
+isdn_tty_modem_reset_regs(modem_info * info, int force)
 {
+	atemu *m = &info->emu;
 	if ((m->mdmreg[12] & 32) || force) {
 		memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
 		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+		info->xmit_size = m->mdmreg[16] * 16;
 	}
-        isdn_tty_modem_reset_vpar(m);
+	isdn_tty_modem_reset_vpar(m);
 	m->mdmcmdl = 0;
 }
 
-static void modem_write_profile(atemu * m)
+static void
+modem_write_profile(atemu * m)
 {
 	memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
 	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
@@ -1553,7 +1654,8 @@
 		send_sig(SIGIO, dev->profd, 1);
 }
 
-int isdn_tty_modem_init(void)
+int
+isdn_tty_modem_init(void)
 {
 	modem *m;
 	int i;
@@ -1610,8 +1712,14 @@
 	}
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		info = &m->info[i];
+		sprintf(info->last_cause, "0000");
+		sprintf(info->last_num, "none");
+		info->last_dir = 0;
+		info->last_lhup = 1;
+		info->last_l2 = 0;
+		info->last_si = 0;
 		isdn_tty_reset_profile(&info->emu);
-		isdn_tty_modem_reset_regs(&info->emu, 1);
+		isdn_tty_modem_reset_regs(info, 1);
 		info->magic = ISDN_ASYNC_MAGIC;
 		info->line = i;
 		info->tty = 0;
@@ -1626,14 +1734,14 @@
 		info->isdn_channel = -1;
 		info->drv_index = -1;
 		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
-                skb_queue_head_init(&info->xmit_queue);
-                skb_queue_head_init(&info->dtmf_queue);
-                if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
-                        printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
-                        return -3;
-                }
-                /* Make room for T.70 header */
-                info->xmit_buf += 4;
+		skb_queue_head_init(&info->xmit_queue);
+		skb_queue_head_init(&info->dtmf_queue);
+		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_SIZE + 5, GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+			return -3;
+		}
+		/* Make room for T.70 header */
+		info->xmit_buf += 4;
 	}
 	return 0;
 }
@@ -1644,61 +1752,46 @@
  * it to the ISDN-Channel.
  * Return Index to dev->mdm or -1 if none found.
  */
-int isdn_tty_find_icall(int di, int ch, char *num)
+int
+isdn_tty_find_icall(int di, int ch, setup_parm setup)
 {
 	char *eaz;
 	int i;
 	int idx;
 	int si1;
 	int si2;
-	char *s;
-	char nr[31];
+	char nr[32];
 	ulong flags;
 
 	save_flags(flags);
 	cli();
-	if (num[0] == ',') {
+	if (!setup.phone[0]) {
 		nr[0] = '0';
-		strncpy(&nr[1], num, 29);
+		nr[1] = '\0';
 		printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
 	} else
-		strncpy(nr, num, 30);
-	s = strtok(nr, ",");
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return -1;
-	}
-	si1 = (int)simple_strtoul(s,NULL,10);
-	s = strtok(NULL, ",");
-	if (!s) {
-		printk(KERN_WARNING "isdn_tty: Incoming callinfo garbled, ignored: %s\n",
-		       num);
-		restore_flags(flags);
-		return -1;
-	}
-	si2 = (int)simple_strtoul(s,NULL,10);
-	eaz = strtok(NULL, ",");
-	if (!eaz) {
+		strcpy(nr, setup.phone);
+	si1 = (int) setup.si1;
+	si2 = (int) setup.si2;
+	if (!setup.eazmsn[0]) {
 		printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
 		eaz = "0";
-	}
+	} else
+		eaz = setup.eazmsn;
 #ifdef ISDN_DEBUG_MODEM_ICALL
 	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
 #endif
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
+		modem_info *info = &dev->mdm.info[i];
 #ifdef ISDN_DEBUG_MODEM_ICALL
 		printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
 		       info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
 		       info->emu.mdmreg[18], info->emu.mdmreg[19]);
 #endif
 		if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
-                             eaz)) &&                             /* EAZ is matching      */
-		    (info->emu.mdmreg[18] & si2bit[si1]) &&       /* SI1 is matching      */
-		    ((info->emu.mdmreg[19] == si2) || !si2)) {    /* SI2 is matching or 0 */
+			     eaz)) &&	/* EAZ is matching      */
+		    (info->emu.mdmreg[18] & si2bit[si1]) &&	/* SI1 is matching      */
+		    ((info->emu.mdmreg[19] == si2) || !si2)) {	/* SI2 is matching or 0 */
 			idx = isdn_dc2minor(di, ch);
 #ifdef ISDN_DEBUG_MODEM_ICALL
 			printk(KERN_DEBUG "m_fi: match1\n");
@@ -1715,9 +1808,11 @@
 				info->drv_index = idx;
 				dev->m_idx[idx] = info->line;
 				dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
-				dev->usage[idx] |= (si1==1)?ISDN_USAGE_VOICE:ISDN_USAGE_MODEM;
+				dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
 				strcpy(dev->num[idx], nr);
-                                info->emu.mdmreg[20] = si2bit[si1];
+				info->emu.mdmreg[20] = si2bit[si1];
+				info->emu.mdmreg[21] = setup.plan;
+				info->emu.mdmreg[22] = setup.screen;
 				isdn_info_update();
 				restore_flags(flags);
 				printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
@@ -1742,7 +1837,8 @@
  * Put a message from the AT-emulator into receive-buffer of tty,
  * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
  */
-static void isdn_tty_at_cout(char *msg, modem_info * info)
+static void
+isdn_tty_at_cout(char *msg, modem_info * info)
 {
 	struct tty_struct *tty;
 	atemu *m = &info->emu;
@@ -1759,17 +1855,17 @@
 	tty = info->tty;
 	for (p = msg; *p; p++) {
 		switch (*p) {
-                        case '\r':
-                                c = m->mdmreg[3];
-                                break;
-                        case '\n':
-                                c = m->mdmreg[4];
-                                break;
-                        case '\b':
-                                c = m->mdmreg[5];
-                                break;
-                        default:
-                                c = *p;
+			case '\r':
+				c = m->mdmreg[3];
+				break;
+			case '\n':
+				c = m->mdmreg[4];
+				break;
+			case '\b':
+				c = m->mdmreg[5];
+				break;
+			default:
+				c = *p;
 		}
 		if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
 			restore_flags(flags);
@@ -1786,24 +1882,26 @@
 /*
  * Perform ATH Hangup
  */
-static void isdn_tty_on_hook(modem_info * info)
+static void
+isdn_tty_on_hook(modem_info * info)
 {
 	if (info->isdn_channel >= 0) {
 #ifdef ISDN_DEBUG_MODEM_HUP
 		printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
 #endif
 		isdn_tty_modem_result(3, info);
-		isdn_tty_modem_hup(info);
+		isdn_tty_modem_hup(info, 1);
 	}
 }
 
-static void isdn_tty_off_hook(void)
+static void
+isdn_tty_off_hook(void)
 {
 	printk(KERN_DEBUG "isdn_tty_off_hook\n");
 }
 
-#define PLUSWAIT1 (HZ/2)	/* 0.5 sec. */
-#define PLUSWAIT2 (HZ*3/2)	/* 1.5 sec */
+#define PLUSWAIT1 (HZ/2)        /* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2)      /* 1.5 sec */
 
 /*
  * Check Buffer for Modem-escape-sequence, activate timer-callback to
@@ -1816,8 +1914,9 @@
  *   pluscount  count of valid escape-characters so far
  *   lastplus   timestamp of last character
  */
-static void isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
-			   int *lastplus, int from_user)
+static void
+isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+		   int *lastplus, int from_user)
 {
 	char cbuf[3];
 
@@ -1860,7 +1959,8 @@
  * For CONNECT-messages also switch to online-mode.
  * For RING-message handle auto-ATA if register 0 != 0
  */
-void isdn_tty_modem_result(int code, modem_info * info)
+void
+isdn_tty_modem_result(int code, modem_info * info)
 {
 	atemu *m = &info->emu;
 	static char *msg[] =
@@ -1868,47 +1968,50 @@
 	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
 	 "RINGING", "NO MSN/EAZ", "VCON"};
 	ulong flags;
-	char s[4];
+	char s[10];
 
 	switch (code) {
-                case 2:
-                        m->mdmreg[1]++;	/* RING */
-                        if (m->mdmreg[1] == m->mdmreg[0])
-                                /* Automatically accept incoming call */
-                                isdn_tty_cmd_ATA(info);
-                        break;
-                case 3:
-                        /* NO CARRIER */
-                        save_flags(flags);
-                        cli();
+		case 2:
+			m->mdmreg[1]++;	/* RING */
+			if (m->mdmreg[1] == m->mdmreg[0])
+				/* Automatically accept incoming call */
+				isdn_tty_cmd_ATA(info);
+			break;
+		case 3:
+			/* NO CARRIER */
+			save_flags(flags);
+			cli();
+			m->mdmreg[1] = 0;
 #ifdef ISDN_DEBUG_MODEM_HUP
-                        printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
-                               (info->flags & ISDN_ASYNC_CLOSING),
-                               (!info->tty));
-#endif
-                        if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
-                                restore_flags(flags);
-                                return;
-                        }
-                        restore_flags(flags);
-                        if (info->vonline & 1) {
-                                /* voice-recording, add DLE-ETX */
-                                isdn_tty_at_cout("\020\003", info);
-                        }
-                        if (info->vonline & 2) {
-                                /* voice-playing, add DLE-DC4 */
-                                isdn_tty_at_cout("\020\024", info);
-                        }
-                        break;
-                case 1:
-                case 5:
-                        if (!info->online)
-                                info->online = 2;
-                        break;
-                case 11:
-                        if (!info->online)
-                                info->online = 1;
-                        break;
+			printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
+			       (info->flags & ISDN_ASYNC_CLOSING),
+			       (!info->tty));
+#endif
+			if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+				restore_flags(flags);
+				return;
+			}
+			restore_flags(flags);
+			if (info->vonline & 1) {
+				/* voice-recording, add DLE-ETX */
+				isdn_tty_at_cout("\020\003", info);
+			}
+			if (info->vonline & 2) {
+				/* voice-playing, add DLE-DC4 */
+				isdn_tty_at_cout("\020\024", info);
+			}
+			break;
+		case 1:
+		case 5:
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 2;
+			break;
+		case 11:
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 1;
+			break;
 	}
 	if (m->mdmreg[12] & 1) {
 		/* Show results */
@@ -1918,17 +2021,38 @@
 			sprintf(s, "%d", code);
 			isdn_tty_at_cout(s, info);
 		} else {
-			if (code == 2) {
+			if ((code == 2) && (!(m->mdmreg[13] & 16))) {
 				isdn_tty_at_cout("CALLER NUMBER: ", info);
 				isdn_tty_at_cout(dev->num[info->drv_index], info);
 				isdn_tty_at_cout("\r\n", info);
 			}
 			isdn_tty_at_cout(msg[code], info);
-			if (code == 5) {
-				/* Append Protocol to CONNECT message */
-				isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
-				if (m->mdmreg[13] & 2)
-					isdn_tty_at_cout("/T.70", info);
+			switch (code) {
+				case 2:
+					/* Print CID only once, _after_ 1.st RING */
+					if ((m->mdmreg[13] & 16) && (m->mdmreg[1] == 1)) {
+						isdn_tty_at_cout("\r\n", info);
+						isdn_tty_at_cout("CALLER NUMBER: ", info);
+						isdn_tty_at_cout(dev->num[info->drv_index], info);
+					}
+					break;
+				case 3:
+				case 6:
+				case 7:
+				case 8:
+					m->mdmreg[1] = 0;
+					/* Append Cause-Message if enabled */
+					if (m->mdmreg[13] & 8) {
+						sprintf(s, "/%s", info->last_cause);
+						isdn_tty_at_cout(s, info);
+					}
+					break;
+				case 5:
+					/* Append Protocol to CONNECT message */
+					isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
+					if (m->mdmreg[13] & 2)
+						isdn_tty_at_cout("/T.70", info);
+					break;
 			}
 		}
 		isdn_tty_at_cout("\r\n", info);
@@ -1940,13 +2064,13 @@
 			restore_flags(flags);
 			return;
 		}
-                if (info->tty->ldisc.flush_buffer)
-                        info->tty->ldisc.flush_buffer(info->tty);
+		if (info->tty->ldisc.flush_buffer)
+			info->tty->ldisc.flush_buffer(info->tty);
 		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
 		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
 		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
 			tty_hangup(info->tty);
-                }
+		}
 		restore_flags(flags);
 	}
 }
@@ -1954,7 +2078,8 @@
 /*
  * Display a modem-register-value.
  */
-static void isdn_tty_show_profile(int ridx, modem_info * info)
+static void
+isdn_tty_show_profile(int ridx, modem_info * info)
 {
 	char v[6];
 
@@ -1965,7 +2090,8 @@
 /*
  * Get MSN-string from char-pointer, set pointer to end of number
  */
-static void isdn_tty_get_msnstr(char *n, char **p)
+static void
+isdn_tty_get_msnstr(char *n, char **p)
 {
 	while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
 		*n++ = *p[0]++;
@@ -1975,7 +2101,8 @@
 /*
  * Get phone-number from modem-commandbuffer
  */
-static void isdn_tty_getdial(char *p, char *q)
+static void
+isdn_tty_getdial(char *p, char *q)
 {
 	int first = 1;
 
@@ -1991,648 +2118,724 @@
 #define PARSE_ERROR { isdn_tty_modem_result(4, info); return; }
 #define PARSE_ERROR1 { isdn_tty_modem_result(4, info); return 1; }
 
+static void
+isdn_tty_report(modem_info * info)
+{
+	atemu *m = &info->emu;
+	char s[80];
+
+	isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
+	sprintf(s, "    Remote Number:    %s\r\n", info->last_num);
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Direction:        %s\r\n", info->last_dir ? "outgoing" : "incoming");
+	isdn_tty_at_cout(s, info);
+	isdn_tty_at_cout("    Layer-2 Protocol: ", info);
+	switch (info->last_l2) {
+		case ISDN_PROTO_L2_X75I:
+			isdn_tty_at_cout("x75i", info);
+			break;
+		case ISDN_PROTO_L2_X75UI:
+			isdn_tty_at_cout("x75ui", info);
+			break;
+		case ISDN_PROTO_L2_X75BUI:
+			isdn_tty_at_cout("x75bui", info);
+			break;
+		case ISDN_PROTO_L2_HDLC:
+			isdn_tty_at_cout("hdlc", info);
+			break;
+		case ISDN_PROTO_L2_TRANS:
+			isdn_tty_at_cout("transparent", info);
+			break;
+		default:
+			isdn_tty_at_cout("unknown", info);
+			break;
+	}
+	isdn_tty_at_cout((m->mdmreg[13] & 2) ? "/t.70\r\n" : "\r\n", info);
+	isdn_tty_at_cout("    Service:          ", info);
+	switch (info->last_si) {
+		case 1:
+			isdn_tty_at_cout("audio\r\n", info);
+			break;
+		case 5:
+			isdn_tty_at_cout("btx\r\n", info);
+			break;
+		case 7:
+			isdn_tty_at_cout("data\r\n", info);
+			break;
+		default:
+			sprintf(s, "%d\r\n", info->last_si);
+			isdn_tty_at_cout(s, info);
+			break;
+	}
+	sprintf(s, "    Hangup location:  %s\r\n", info->last_lhup ? "local" : "remote");
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Last cause:       %s\r\n", info->last_cause);
+	isdn_tty_at_cout(s, info);
+}
+
 /*
  * Parse AT&.. commands.
  */
-static int isdn_tty_cmd_ATand(char **p, modem_info * info)
+static int
+isdn_tty_cmd_ATand(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        int i;
-        char rb[100];
-
-        switch (*p[0]) {
-                case 'B':
-                        /* &B - Set Buffersize */
-                        p[0]++;
-                        i = isdn_getnum(p);
-                        if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
-                                PARSE_ERROR1;
+	atemu *m = &info->emu;
+	int i;
+	char rb[100];
+
+	switch (*p[0]) {
+		case 'B':
+			/* &B - Set Buffersize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i < 0) || (i > ISDN_SERIAL_XMIT_SIZE))
+				PARSE_ERROR1;
 #ifdef CONFIG_ISDN_AUDIO
-                        if ((m->mdmreg[18] & 1) && (i > VBUF))
-                                PARSE_ERROR1;
+			if ((m->mdmreg[18] & 1) && (i > VBUF))
+				PARSE_ERROR1;
 #endif
-                        m->mdmreg[16] = i / 16;
-                        info->xmit_size = m->mdmreg[16] * 16;
-                        break;
-                case 'D':
-                        /* &D - Set DCD-Low-behavior */
-                        p[0]++;
-                        switch (isdn_getnum(p)) {
-                                case 0:
-                                        m->mdmreg[13] &= ~4;
-                                        m->mdmreg[12] &= ~32;
-                                        break;
-                                case 2:
-                                        m->mdmreg[13] |= 4;
-                                        m->mdmreg[12] &= ~32;
-                                        break;
-                                case 3:
-                                        m->mdmreg[13] |= 4;
-                                        m->mdmreg[12] |= 32;
-                                        break;
-                                default:
-                                        PARSE_ERROR1
-                        }
-                        break;
-                case 'E':
-                        /* &E -Set EAZ/MSN */
-                        p[0]++;
-                        isdn_tty_get_msnstr(m->msn, p);
-                        break;
-                case 'F':
-                        /* &F -Set Factory-Defaults */
-                        p[0]++;
-                        isdn_tty_reset_profile(m);
-                        isdn_tty_modem_reset_regs(m, 1);
-                        break;
-                case 'S':
-                        /* &S - Set Windowsize */
-                        p[0]++;
-                        i = isdn_getnum(p);
-                        if ((i > 0) && (i < 9))
-                                m->mdmreg[17] = i;
-                        else
-                                PARSE_ERROR1;
-                        break;
-                case 'V':
-                        /* &V - Show registers */
-                        p[0]++;
-                        for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
-                                sprintf(rb, "S%d=%d%s", i, 
-                                        m->mdmreg[i], (i == 6) ? "\r\n" : " ");
-                                isdn_tty_at_cout(rb, info);
-                        }
-                        sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
-                                strlen(m->msn) ? m->msn : "None");
-                        isdn_tty_at_cout(rb, info);
-                        break;
-                case 'W':
-                        /* &W - Write Profile */
-                        p[0]++;
-                        switch (*p[0]) {
-                                case '0':
-                                        p[0]++;
-                                        modem_write_profile(m);
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 'X':
-                        /* &X - Switch to BTX-Mode */
-                        p[0]++;
-                        switch (isdn_getnum(p)) {
-                                case 0:
-                                        m->mdmreg[13] &= ~2;
-                                        info->xmit_size = m->mdmreg[16] * 16;
-                                        break;
-                                case 1:
-                                        m->mdmreg[13] |= 2;
-                                        m->mdmreg[14] = 0;
-                                        info->xmit_size = 112;
-                                        m->mdmreg[18] = 4;
-                                        m->mdmreg[19] = 0;
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                default:
-                        PARSE_ERROR1;
-        }
-        return 0;
+			m->mdmreg[16] = i / 16;
+			info->xmit_size = m->mdmreg[16] * 16;
+			break;
+		case 'D':
+			/* &D - Set DCD-Low-behavior */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[13] &= ~4;
+					m->mdmreg[12] &= ~32;
+					break;
+				case 2:
+					m->mdmreg[13] |= 4;
+					m->mdmreg[12] &= ~32;
+					break;
+				case 3:
+					m->mdmreg[13] |= 4;
+					m->mdmreg[12] |= 32;
+					break;
+				default:
+					PARSE_ERROR1
+			}
+			break;
+		case 'E':
+			/* &E -Set EAZ/MSN */
+			p[0]++;
+			isdn_tty_get_msnstr(m->msn, p);
+			break;
+		case 'F':
+			/* &F -Set Factory-Defaults */
+			p[0]++;
+			isdn_tty_reset_profile(m);
+			isdn_tty_modem_reset_regs(info, 1);
+			break;
+		case 'S':
+			/* &S - Set Windowsize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i > 0) && (i < 9))
+				m->mdmreg[17] = i;
+			else
+				PARSE_ERROR1;
+			break;
+		case 'V':
+			/* &V - Show registers */
+			p[0]++;
+			for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
+				sprintf(rb, "S%d=%d%s", i,
+				  m->mdmreg[i], (i == 6) ? "\r\n" : " ");
+				isdn_tty_at_cout(rb, info);
+			}
+			sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
+				strlen(m->msn) ? m->msn : "None");
+			isdn_tty_at_cout(rb, info);
+			break;
+		case 'W':
+			/* &W - Write Profile */
+			p[0]++;
+			switch (*p[0]) {
+				case '0':
+					p[0]++;
+					modem_write_profile(m);
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 'X':
+			/* &X - Switch to BTX-Mode */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[13] &= ~2;
+					info->xmit_size = m->mdmreg[16] * 16;
+					break;
+				case 1:
+					m->mdmreg[13] |= 2;
+					m->mdmreg[14] = 0;
+					info->xmit_size = 112;
+					m->mdmreg[18] = 4;
+					m->mdmreg[19] = 0;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
 }
 
 /*
  * Perform ATS command
  */
-static int isdn_tty_cmd_ATS(char **p, modem_info * info)
+static int
+isdn_tty_cmd_ATS(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        int mreg;
-        int mval;
-
-        mreg = isdn_getnum(p);
-        if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
-                PARSE_ERROR1;
-        switch (*p[0]) {
-                case '=':
-                        p[0]++;
-                        mval = isdn_getnum(p);
-                        if (mval < 0 || mval > 255)
-                                PARSE_ERROR1;
-                        switch (mreg) {
-                                /* Some plausibility checks */
-                                case 14:
-                                        if (mval > ISDN_PROTO_L2_TRANS)
-                                                PARSE_ERROR1;
-                                        break;
-                                case 16:
-                                        if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
-                                                PARSE_ERROR1;
+	atemu *m = &info->emu;
+	int mreg;
+	int mval;
+
+	mreg = isdn_getnum(p);
+	if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
+		PARSE_ERROR1;
+	switch (*p[0]) {
+		case '=':
+			p[0]++;
+			mval = isdn_getnum(p);
+			if (mval < 0 || mval > 255)
+				PARSE_ERROR1;
+			switch (mreg) {
+					/* Some plausibility checks */
+				case 14:
+					if (mval > ISDN_PROTO_L2_TRANS)
+						PARSE_ERROR1;
+					break;
+				case 16:
+					if ((mval * 16) > ISDN_SERIAL_XMIT_SIZE)
+						PARSE_ERROR1;
 #ifdef CONFIG_ISDN_AUDIO
-                                        if ((m->mdmreg[18] & 1) && (mval > VBUFX))
-                                                PARSE_ERROR1;
+					if ((m->mdmreg[18] & 1) && (mval > VBUFX))
+						PARSE_ERROR1;
 #endif
-                                        info->xmit_size = mval * 16;
-                                        break;
-                                case 20:
-                                        PARSE_ERROR1;
-                        }
-                        m->mdmreg[mreg] = mval;
-                        break;
-                case '?':
-                        p[0]++;
-                        isdn_tty_show_profile(mreg, info);
-                        break;
-                default:
-                        PARSE_ERROR1;
-                        break;
-        }
-        return 0;
+					info->xmit_size = mval * 16;
+					break;
+				case 20:
+					PARSE_ERROR1;
+			}
+			m->mdmreg[mreg] = mval;
+			break;
+		case '?':
+			p[0]++;
+			isdn_tty_show_profile(mreg, info);
+			break;
+		default:
+			PARSE_ERROR1;
+			break;
+	}
+	return 0;
 }
 
 /*
  * Perform ATA command
  */
-static void isdn_tty_cmd_ATA(modem_info * info)
+static void
+isdn_tty_cmd_ATA(modem_info * info)
 {
-        atemu *m = &info->emu;
-        isdn_ctrl cmd;
-        int l2;
-
-        if (info->msr & UART_MSR_RI) {
-                /* Accept incoming call */
-                m->mdmreg[1] = 0;
-                info->msr &= ~UART_MSR_RI;
-                l2 = m->mdmreg[14];
+	atemu *m = &info->emu;
+	isdn_ctrl cmd;
+	int l2;
+
+	if (info->msr & UART_MSR_RI) {
+		/* Accept incoming call */
+		info->last_dir = 0;
+		strcpy(info->last_num, dev->num[info->drv_index]);
+		m->mdmreg[1] = 0;
+		info->msr &= ~UART_MSR_RI;
+		l2 = m->mdmreg[14];
 #ifdef CONFIG_ISDN_AUDIO
-                /* If more than one bit set in reg18, autoselect Layer2 */
-                if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18])
-                        if (m->mdmreg[20] == 1) l2 = 4;
-#endif
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL2;
-                cmd.arg = info->isdn_channel + (l2 << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.command = ISDN_CMD_SETL3;
-                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-                cmd.driver = info->isdn_driver;
-                cmd.arg = info->isdn_channel;
-                cmd.command = ISDN_CMD_ACCEPTD;
-                dev->drv[info->isdn_driver]->interface->command(&cmd);
-        } else
-                isdn_tty_modem_result(8, info);
+		/* If more than one bit set in reg18, autoselect Layer2 */
+		if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18])
+			if (m->mdmreg[20] == 1)
+				l2 = 4;
+#endif
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		info->last_l2 = l2;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_ACCEPTD;
+		dev->drv[info->isdn_driver]->interface->command(&cmd);
+	} else
+		isdn_tty_modem_result(8, info);
 }
 
 #ifdef CONFIG_ISDN_AUDIO
 /*
  * Parse AT+F.. commands
  */
-static int isdn_tty_cmd_PLUSF(char **p, modem_info * info)
+static int
+isdn_tty_cmd_PLUSF(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        int par;
+	atemu *m = &info->emu;
+	int par;
 	char rs[20];
 
-        if (!strncmp(p[0],"CLASS",5)) {
-                p[0] += 5;
-                switch (*p[0]) {
-                        case '?':
-                                p[0]++;
-                                sprintf(rs,"\r\n%d",
-                                        (m->mdmreg[18]&1)?8:0);
-                                isdn_tty_at_cout(rs, info);
-                                break;
-                        case '=':
-                                p[0]++;
-                                switch (*p[0]) {
-                                        case '0':
-                                                p[0]++;
-                                                m->mdmreg[18] = 4;
-                                                info->xmit_size =
-                                                        m->mdmreg[16] * 16;
-                                                break;
-                                        case '8':
-                                                p[0]++;
-                                                m->mdmreg[18] = 5;
-                                                info->xmit_size = VBUF;
-                                                break;
-                                        case '?':
-                                                p[0]++;
-                                                isdn_tty_at_cout("\r\n0,8",
-                                                                 info);
-                                                break;
-                                        default:
-                                                PARSE_ERROR1;
-                                }
-                                break;
-                        default:
-                                PARSE_ERROR1;
-                }
-                return 0;
-        }        
-        if (!strncmp(p[0],"AA",2)) {
-                p[0] += 2;
-                switch (*p[0]) {
-                        case '?':
-                                p[0]++;
-                                sprintf(rs,"\r\n%d",
-                                        m->mdmreg[0]);
-                                isdn_tty_at_cout(rs, info);
-                                break;
-                        case '=':
-                                p[0]++;
-                                par = isdn_getnum(p);
-                                if ((par < 0) || (par > 255))
-                                        PARSE_ERROR1;
-                                m->mdmreg[0]=par;
-                                break;
-                        default:
-                                PARSE_ERROR1;                                
-                }
-                return 0;
-        }
-        PARSE_ERROR1;
+	if (!strncmp(p[0], "CLASS", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d",
+					(m->mdmreg[18] & 1) ? 8 : 0);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				switch (*p[0]) {
+					case '0':
+						p[0]++;
+						m->mdmreg[18] = 4;
+						info->xmit_size =
+						    m->mdmreg[16] * 16;
+						break;
+					case '8':
+						p[0]++;
+						m->mdmreg[18] = 5;
+						info->xmit_size = VBUF;
+						break;
+					case '?':
+						p[0]++;
+						isdn_tty_at_cout("\r\n0,8",
+								 info);
+						break;
+					default:
+						PARSE_ERROR1;
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	if (!strncmp(p[0], "AA", 2)) {
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d",
+					m->mdmreg[0]);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				m->mdmreg[0] = par;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	PARSE_ERROR1;
 }
 
 /*
  * Parse AT+V.. commands
  */
-static int isdn_tty_cmd_PLUSV(char **p, modem_info * info)
+static int
+isdn_tty_cmd_PLUSV(char **p, modem_info * info)
 {
-        atemu *m = &info->emu;
-        static char *vcmd[] = {"NH","IP","LS","RX","SD","SM","TX",NULL};
-        int i;
+	atemu *m = &info->emu;
+	static char *vcmd[] =
+	{"NH", "IP", "LS", "RX", "SD", "SM", "TX", NULL};
+	int i;
 	int par1;
 	int par2;
 	char rs[20];
 
-        i = 0;
-        while (vcmd[i]) {
-                if (!strncmp(vcmd[i],p[0],2)) {
-                        p[0] += 2;
-                        break;
-                }
-                i++;
-        }
-        switch (i) {
-                case 0:
-                        /* AT+VNH - Auto hangup feature */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        isdn_tty_at_cout("\r\n1", info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '1':
-                                                        p[0]++;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n1", info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 1:
-                        /* AT+VIP - Reset all voice parameters */
-                        isdn_tty_modem_reset_vpar(m);
-                        break;
-                case 2:
-                        /* AT+VLS - Select device, accept incoming call */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        sprintf(rs,"\r\n%d",m->vpar[0]);
-                                        isdn_tty_at_cout(rs, info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '0':
-                                                        p[0]++;
-                                                        m->vpar[0] = 0;
-                                                        break;
-                                                case '2':
-                                                        p[0]++;
-                                                        m->vpar[0] = 2;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n0,2", info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 3:
-                        /* AT+VRX - Start recording */
-                        if (!m->vpar[0])
-                                PARSE_ERROR1;
-                        if (info->online != 1) {
-                                isdn_tty_modem_result(8, info);
-                                return 1;
-                        }
-                        info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
-                        if (!info->dtmf_state) {
-                                printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
-                                PARSE_ERROR1;
-                        }
-                        if (m->vpar[3] < 5) {
-                                info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
-                                if (!info->adpcmr) {
-                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
-                                        PARSE_ERROR1;
-                                }
-                        }
-                        info->vonline = 1;
-                        isdn_tty_modem_result(1, info);
-                        return 1;
-                        break;
-                case 4:
-                        /* AT+VSD - Silence detection */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        sprintf(rs,"\r\n<%d>,<%d>",
-                                                m->vpar[1],
-                                                m->vpar[2]);
-                                        isdn_tty_at_cout(rs, info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '0':
-                                                case '1':
-                                                case '2':
-                                                case '3':
-                                                        par1 = isdn_getnum(p);
-                                                        if ((par1 < 0) || (par1 > 31))
-                                                                PARSE_ERROR1;
-                                                        if (*p[0] != ',')
-                                                                PARSE_ERROR1;
-                                                        p[0]++;
-                                                        par2 = isdn_getnum(p);
-                                                        if ((par2 < 0) || (par2 > 255))
-                                                                PARSE_ERROR1;
-                                                        m->vpar[1] = par1;
-                                                        m->vpar[2] = par2;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n<0-31>,<0-255>",
-                                                                         info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 5:
-                        /* AT+VSM - Select compression */
-                        switch (*p[0]) {
-                                case '?':
-                                        p[0]++;
-                                        sprintf(rs,"\r\n<%d>,<%d><8000>",
-                                                m->vpar[3],
-                                                m->vpar[1]);
-                                        isdn_tty_at_cout(rs, info);
-                                        break;
-                                case '=':
-                                        p[0]++;
-                                        switch (*p[0]) {
-                                                case '2':
-                                                case '3':
-                                                case '4':
-                                                case '5':
-                                                case '6':
-                                                        par1 = isdn_getnum(p);
-                                                        if ((par1 < 2) || (par1 > 6))
-                                                                PARSE_ERROR1;
-                                                        m->vpar[3] = par1;
-                                                        break;
-                                                case '?':
-                                                        p[0]++;
-                                                        isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
-                                                                         info);
-                                                        isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
-                                                                         info);
-                                                        isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
-                                                                         info);
-                                                        isdn_tty_at_cout("5;ALAW;8;0;(8000)",
-                                                                         info);
-                                                        isdn_tty_at_cout("6;ULAW;8;0;(8000)",
-                                                                         info);
-                                                        break;
-                                                default:
-                                                        PARSE_ERROR1;
-                                        }
-                                        break;
-                                default:
-                                        PARSE_ERROR1;
-                        }
-                        break;
-                case 6:
-                        /* AT+VTX - Start sending */
-                        if (!m->vpar[0])
-                                PARSE_ERROR1;
-                        if (info->online != 1) {
-                                isdn_tty_modem_result(8, info);
-                                return 1;
-                        }
-                        info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
-                        if (!info->dtmf_state) {
-                                printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
-                                PARSE_ERROR1;
-                        }
-                        if (m->vpar[3] < 5) {
-                                info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
-                                if (!info->adpcms) {
-                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
-                                        PARSE_ERROR1;
-                                }
-                        }
-                        m->lastDLE = 0;
-                        info->vonline = 2;
-                        isdn_tty_modem_result(1, info);
-                        return 1;
-                        break;
-                default:
-                        PARSE_ERROR1;
-        }
-        return 0;
+	i = 0;
+	while (vcmd[i]) {
+		if (!strncmp(vcmd[i], p[0], 2)) {
+			p[0] += 2;
+			break;
+		}
+		i++;
+	}
+	switch (i) {
+		case 0:
+			/* AT+VNH - Auto hangup feature */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					isdn_tty_at_cout("\r\n1", info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '1':
+							p[0]++;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n1", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 1:
+			/* AT+VIP - Reset all voice parameters */
+			isdn_tty_modem_reset_vpar(m);
+			break;
+		case 2:
+			/* AT+VLS - Select device, accept incoming call */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n%d", m->vpar[0]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '0':
+							p[0]++;
+							m->vpar[0] = 0;
+							break;
+						case '2':
+							p[0]++;
+							m->vpar[0] = 2;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n0,2", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 3:
+			/* AT+VRX - Start recording */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(8, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
+				if (!info->adpcmr) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VRX\n");
+#endif
+			info->vonline |= 1;
+			isdn_tty_modem_result(1, info);
+			return 0;
+			break;
+		case 4:
+			/* AT+VSD - Silence detection */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d>",
+						m->vpar[1],
+						m->vpar[2]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '0':
+						case '1':
+						case '2':
+						case '3':
+							par1 = isdn_getnum(p);
+							if ((par1 < 0) || (par1 > 31))
+								PARSE_ERROR1;
+							if (*p[0] != ',')
+								PARSE_ERROR1;
+							p[0]++;
+							par2 = isdn_getnum(p);
+							if ((par2 < 0) || (par2 > 255))
+								PARSE_ERROR1;
+							m->vpar[1] = par1;
+							m->vpar[2] = par2;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+								   info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 5:
+			/* AT+VSM - Select compression */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d><8000>",
+						m->vpar[3],
+						m->vpar[1]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '2':
+						case '3':
+						case '4':
+						case '5':
+						case '6':
+							par1 = isdn_getnum(p);
+							if ((par1 < 2) || (par1 > 6))
+								PARSE_ERROR1;
+							m->vpar[3] = par1;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("5;ALAW;8;0;(8000)",
+								   info);
+							isdn_tty_at_cout("6;ULAW;8;0;(8000)",
+								   info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 6:
+			/* AT+VTX - Start sending */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(8, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
+				if (!info->adpcms) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VTX\n");
+#endif
+			m->lastDLE = 0;
+			info->vonline |= 2;
+			isdn_tty_modem_result(1, info);
+			return 0;
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
 }
-#endif        /* CONFIG_ISDN_AUDIO */
+#endif                          /* CONFIG_ISDN_AUDIO */
 
 /*
  * Parse and perform an AT-command-line.
  */
-static void isdn_tty_parse_at(modem_info * info)
+static void
+isdn_tty_parse_at(modem_info * info)
 {
-        atemu *m = &info->emu;
-        char *p;
-        char ds[40];
+	atemu *m = &info->emu;
+	char *p;
+	char ds[40];
 
 #ifdef ISDN_DEBUG_AT
-        printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+	printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
 #endif
-        for (p = &m->mdmcmd[2]; *p;) {
-                switch (*p) {
-                        case 'A':
-                                /* A - Accept incoming call */
-                                p++;
-                                isdn_tty_cmd_ATA(info);
-                                return;
-                                break;
-                        case 'D':
-                                /* D - Dial */
-                                isdn_tty_getdial(++p, ds);
-                                p += strlen(p);
-                                if (!strlen(m->msn))
-                                        isdn_tty_modem_result(10, info);
-                                else if (strlen(ds))
-                                        isdn_tty_dial(ds, info, m);
-                                else
-                                        isdn_tty_modem_result(4, info);
-                                return;
-                        case 'E':
-                                /* E - Turn Echo on/off */
-                                p++;
-                                switch (isdn_getnum(&p)) {
-                                        case 0:
-                                                m->mdmreg[12] &= ~4;
-                                                break;
-                                        case 1:
-                                                m->mdmreg[12] |= 4;
-                                                break;
-                                        default:
-                                                PARSE_ERROR;
-                                }
-                                break;
-                        case 'H':
-                                /* H - On/Off-hook */
-                                p++;
-                                switch (*p) {
-                                        case '0':
-                                                p++;
-                                                isdn_tty_on_hook(info);
-                                                break;
-                                        case '1':
-                                                p++;
-                                                isdn_tty_off_hook();
-                                                break;
-                                        default:
-                                                isdn_tty_on_hook(info);
-                                                break;
-                                }
-                                break;
-                        case 'I':
-                                /* I - Information */
-                                p++;
-                                isdn_tty_at_cout("\r\nLinux ISDN", info);
-                                switch (*p) {
-                                        case '0':
-                                        case '1':
-                                                p++;
-                                                break;
-                                        default:
-                                }
-                                break;
-                        case 'O':
-                                /* O - Go online */
-                                p++;
-                                if (info->msr & UART_MSR_DCD)
-                                        /* if B-Channel is up */
-                                        isdn_tty_modem_result(5, info);
-                                else
-                                        isdn_tty_modem_result(3, info);
-                                return;
-                        case 'Q':
-                                /* Q - Turn Emulator messages on/off */
-                                p++;
-                                switch (isdn_getnum(&p)) {
-                                        case 0:
-                                                m->mdmreg[12] |= 1;
-                                                break;
-                                        case 1:
-                                                m->mdmreg[12] &= ~1;
-                                                break;
-                                        default:
-                                                PARSE_ERROR;
-                                }
-                                break;
-                        case 'S':
-                                /* S - Set/Get Register */
-                                p++;
-                                if (isdn_tty_cmd_ATS(&p, info))
-                                        return;
-                                break;
-                        case 'V':
-                                /* V - Numeric or ASCII Emulator-messages */
-                                p++;
-                                switch (isdn_getnum(&p)) {
-                                        case 0:
-                                                m->mdmreg[12] |= 2;
-                                                break;
-                                        case 1:
-                                                m->mdmreg[12] &= ~2;
-                                                break;
-                                        default:
-                                                PARSE_ERROR;
-                                }
-                                break;
-                        case 'Z':
-                                /* Z - Load Registers from Profile */
-                                p++;
-                                isdn_tty_modem_reset_regs(m, 1);
-                                break;
+	for (p = &m->mdmcmd[2]; *p;) {
+		switch (*p) {
+			case 'A':
+				/* A - Accept incoming call */
+				p++;
+				isdn_tty_cmd_ATA(info);
+				return;
+				break;
+			case 'D':
+				/* D - Dial */
+				isdn_tty_getdial(++p, ds);
+				p += strlen(p);
+				if (!strlen(m->msn))
+					isdn_tty_modem_result(10, info);
+				else if (strlen(ds))
+					isdn_tty_dial(ds, info, m);
+				else
+					isdn_tty_modem_result(4, info);
+				return;
+			case 'E':
+				/* E - Turn Echo on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[12] &= ~4;
+						break;
+					case 1:
+						m->mdmreg[12] |= 4;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'H':
+				/* H - On/Off-hook */
+				p++;
+				switch (*p) {
+					case '0':
+						p++;
+						isdn_tty_on_hook(info);
+						break;
+					case '1':
+						p++;
+						isdn_tty_off_hook();
+						break;
+					default:
+						isdn_tty_on_hook(info);
+						break;
+				}
+				break;
+			case 'I':
+				/* I - Information */
+				p++;
+				isdn_tty_at_cout("\r\nLinux ISDN", info);
+				switch (*p) {
+					case '0':
+					case '1':
+						p++;
+						break;
+					case '2':
+						p++;
+						isdn_tty_report(info);
+						break;
+					default:
+				}
+				break;
+			case 'O':
+				/* O - Go online */
+				p++;
+				if (info->msr & UART_MSR_DCD)
+					/* if B-Channel is up */
+					isdn_tty_modem_result(5, info);
+				else
+					isdn_tty_modem_result(3, info);
+				return;
+			case 'Q':
+				/* Q - Turn Emulator messages on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[12] |= 1;
+						break;
+					case 1:
+						m->mdmreg[12] &= ~1;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'S':
+				/* S - Set/Get Register */
+				p++;
+				if (isdn_tty_cmd_ATS(&p, info))
+					return;
+				break;
+			case 'V':
+				/* V - Numeric or ASCII Emulator-messages */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[12] |= 2;
+						break;
+					case 1:
+						m->mdmreg[12] &= ~2;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'Z':
+				/* Z - Load Registers from Profile */
+				p++;
+				isdn_tty_modem_reset_regs(info, 1);
+				break;
 #ifdef CONFIG_ISDN_AUDIO
-                        case '+':
-                                p++;
-                                switch (*p) {
-                                        case 'F':
-                                                p++;
-                                                if (isdn_tty_cmd_PLUSF(&p, info))
-                                                        return;
-                                                break;
-                                        case 'V':
-                                                if (!(m->mdmreg[18] & 1))
-                                                        PARSE_ERROR;
-                                                p++;
-                                                if (isdn_tty_cmd_PLUSV(&p, info))
-                                                        return;
-                                                break;
-                                }
-                                break;
-#endif        /* CONFIG_ISDN_AUDIO */
-                        case '&':
-                                p++;
-                                if (isdn_tty_cmd_ATand(&p, info))
-                                        return;
-                                break;
-                        default:
-                                isdn_tty_modem_result(4, info);
-                                return;
-                }
-        }
-        isdn_tty_modem_result(0, info);
+			case '+':
+				p++;
+				switch (*p) {
+					case 'F':
+						p++;
+						if (isdn_tty_cmd_PLUSF(&p, info))
+							return;
+						break;
+					case 'V':
+						if (!(m->mdmreg[18] & 1))
+							PARSE_ERROR;
+						p++;
+						if (isdn_tty_cmd_PLUSV(&p, info))
+							return;
+						break;
+				}
+				break;
+#endif                          /* CONFIG_ISDN_AUDIO */
+			case '&':
+				p++;
+				if (isdn_tty_cmd_ATand(&p, info))
+					return;
+				break;
+			default:
+				isdn_tty_modem_result(4, info);
+				return;
+		}
+	}
+	isdn_tty_modem_result(0, info);
 }
 
 /* Need own toupper() because standard-toupper is not available
@@ -2649,7 +2852,8 @@
  *   channel  index to line (minor-device)
  *   user     flag: buffer is in userspace
  */
-static int isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
+static int
+isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
 {
 	atemu *m = &info->emu;
 	int total = 0;
@@ -2659,7 +2863,7 @@
 
 	for (cnt = count; cnt > 0; p++, cnt--) {
 		if (user)
-		    get_user(c, p);
+			GET_USER(c, p);
 		else
 			c = *p;
 		total++;
@@ -2695,16 +2899,16 @@
 			if (m->mdmcmdl < 255) {
 				c = my_toupper(c);
 				switch (m->mdmcmdl) {
-                                        case 0:
-                                                if (c == 'A')
-                                                        m->mdmcmd[m->mdmcmdl++] = c;
-                                                break;
-                                        case 1:
-                                                if (c == 'T')
-                                                        m->mdmcmd[m->mdmcmdl++] = c;
-                                                break;
-                                        default:
-                                                m->mdmcmd[m->mdmcmdl++] = c;
+					case 0:
+						if (c == 'A')
+							m->mdmcmd[m->mdmcmdl++] = c;
+						break;
+					case 1:
+						if (c == 'T')
+							m->mdmcmd[m->mdmcmdl++] = c;
+						break;
+					default:
+						m->mdmcmd[m->mdmcmdl++] = c;
 				}
 			}
 		}
@@ -2715,10 +2919,11 @@
 /*
  * Switch all modem-channels who are online and got a valid
  * escape-sequence 1.5 seconds ago, to command-mode.
- * This function is called every second via timer-interrupt from within 
+ * This function is called every second via timer-interrupt from within
  * timer-dispatcher isdn_timer_function()
  */
-void isdn_tty_modem_escape(void)
+void
+isdn_tty_modem_escape(void)
 {
 	int ton = 0;
 	int i;
@@ -2727,7 +2932,7 @@
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
 		if (USG_MODEM(dev->usage[i]))
 			if ((midx = dev->m_idx[i]) >= 0) {
-                                modem_info *info = &dev->mdm.info[midx];
+				modem_info *info = &dev->mdm.info[midx];
 				if (info->online) {
 					ton = 1;
 					if ((info->emu.pluscount == 3) &&
@@ -2737,27 +2942,28 @@
 						isdn_tty_modem_result(0, info);
 					}
 				}
-                        }
+			}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
 }
 
 /*
  * Put a RING-message to all modem-channels who have the RI-bit set.
- * This function is called every second via timer-interrupt from within 
+ * This function is called every second via timer-interrupt from within
  * timer-dispatcher isdn_timer_function()
  */
-void isdn_tty_modem_ring(void)
+void
+isdn_tty_modem_ring(void)
 {
 	int ton = 0;
 	int i;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
-                if (info->msr & UART_MSR_RI) {
-                        ton = 1;
-                        isdn_tty_modem_result(2, info);
-                }
-        }
+		modem_info *info = &dev->mdm.info[i];
+		if (info->msr & UART_MSR_RI) {
+			ton = 1;
+			isdn_tty_modem_result(2, info);
+		}
+	}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
 }
 
@@ -2765,19 +2971,20 @@
  * For all online tty's, try sending data to
  * the lower levels.
  */
-void isdn_tty_modem_xmit(void)
+void
+isdn_tty_modem_xmit(void)
 {
 	int ton = 1;
 	int i;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
-                if (info->online) {
-                        ton = 1;
-                        isdn_tty_senddown(info);
-                        isdn_tty_tint(info);
-                }
-        }
+		modem_info *info = &dev->mdm.info[i];
+		if (info->online) {
+			ton = 1;
+			isdn_tty_senddown(info);
+			isdn_tty_tint(info);
+		}
+	}
 	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
 }
 
@@ -2786,20 +2993,21 @@
  * Search the tty-devices for an appropriate device, decrement its
  * counter for outstanding packets, and set CTS.
  */
-void isdn_tty_bsent(int drv, int chan)
+void
+isdn_tty_bsent(int drv, int chan)
 {
 	int i;
 
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-                modem_info *info = &dev->mdm.info[i];
-                if ((info->isdn_driver == drv) &&
-                    (info->isdn_channel == chan) ) {
-                        info->msr |= UART_MSR_CTS;
-                        if (info->send_outstanding)
-                                if (!(--info->send_outstanding))
-                                        info->lsr |= UART_LSR_TEMT;
-                        isdn_tty_tint(info);
-                }
-        }
+		modem_info *info = &dev->mdm.info[i];
+		if ((info->isdn_driver == drv) &&
+		    (info->isdn_channel == chan)) {
+			info->msr |= UART_MSR_CTS;
+			if (info->send_outstanding)
+				if (!(--info->send_outstanding))
+					info->lsr |= UART_LSR_TEMT;
+			isdn_tty_tint(info);
+		}
+	}
 	return;
 }
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/isdn_tty.h linux/drivers/isdn/isdn_tty.h
--- v2.1.26/linux/drivers/isdn/isdn_tty.h	Sun May 19 05:29:29 1996
+++ linux/drivers/isdn/isdn_tty.h	Tue Feb 25 17:12:50 1997
@@ -1,10 +1,10 @@
-/* $Id: isdn_tty.h,v 1.5 1996/05/17 03:52:31 fritz Exp $
- *
+/* $Id: isdn_tty.h,v 1.8 1997/02/10 20:12:50 fritz Exp $
+
  * header for Linux ISDN subsystem, tty related functions (linklevel).
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- * 
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -17,9 +17,18 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * $Log: isdn_tty.h,v $
+ * Revision 1.8  1997/02/10 20:12:50  fritz
+ * Changed interface for reporting incoming calls.
+ *
+ * Revision 1.7  1997/02/03 23:06:10  fritz
+ * Reformatted according CodingStyle
+ *
+ * Revision 1.6  1997/01/14 01:35:19  fritz
+ * Changed prototype of isdn_tty_modem_hup.
+ *
  * Revision 1.5  1996/05/17 03:52:31  fritz
  * Changed DLE handling for audio receive.
  *
@@ -37,15 +46,15 @@
  *
  */
 
-extern void  isdn_tty_modem_result(int, modem_info *);
-extern void  isdn_tty_modem_escape(void);
-extern void  isdn_tty_modem_ring(void);
-extern void  isdn_tty_modem_xmit(void);
-extern void  isdn_tty_modem_hup(modem_info *);
-extern int   isdn_tty_modem_init(void);
-extern void  isdn_tty_readmodem(void);
-extern int   isdn_tty_try_read(modem_info *, struct sk_buff *);
-extern int   isdn_tty_find_icall(int, int, char *);
-extern int   isdn_tty_countDLE(unsigned char *, int);
-extern void  isdn_tty_bsent(int, int);
-extern void  isdn_tty_cleanup_xmit(modem_info *);
+extern void isdn_tty_modem_result(int, modem_info *);
+extern void isdn_tty_modem_escape(void);
+extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_modem_xmit(void);
+extern void isdn_tty_modem_hup(modem_info *, int);
+extern int isdn_tty_modem_init(void);
+extern void isdn_tty_readmodem(void);
+extern int isdn_tty_try_read(modem_info *, struct sk_buff *);
+extern int isdn_tty_find_icall(int, int, setup_parm);
+extern int isdn_tty_countDLE(unsigned char *, int);
+extern void isdn_tty_bsent(int, int);
+extern void isdn_tty_cleanup_xmit(modem_info *);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/pcbit/callbacks.c linux/drivers/isdn/pcbit/callbacks.c
--- v2.1.26/linux/drivers/isdn/pcbit/callbacks.c	Sat Jun 29 10:36:22 1996
+++ linux/drivers/isdn/pcbit/callbacks.c	Tue Feb 25 17:12:50 1997
@@ -164,18 +164,12 @@
          *  ictl.num >= strlen() + strlen() + 5
          */
 
-        if (cbdata->data.setup.CalledPN) 
-		sprintf(ictl.num, "%s,%d,%d,%s", 
-			cbdata->data.setup.CallingPN, 
-			7, 0, 
-			cbdata->data.setup.CalledPN);
-	
-	else
-		sprintf(ictl.num, "%s,%d,%d,%s", 
-			cbdata->data.setup.CallingPN,
-			7, 0, 
-			"0");
-
+		strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+		strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
+		ictl.parm.setup.si1 = 7;
+		ictl.parm.setup.si2 = 0;
+		ictl.parm.setup.plan = 0;
+		ictl.parm.setup.screen = 0;
 
 #ifdef DEBUG
 	printk(KERN_DEBUG "statstr: %s\n", ictl.num);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/pcbit/capi.c linux/drivers/isdn/pcbit/capi.c
--- v2.1.26/linux/drivers/isdn/pcbit/capi.c	Sat Jun 29 10:36:22 1996
+++ linux/drivers/isdn/pcbit/capi.c	Tue Feb 25 17:12:50 1997
@@ -147,7 +147,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
@@ -170,7 +170,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -200,7 +200,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -222,7 +222,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
   
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -285,7 +285,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -338,7 +338,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
 
@@ -357,7 +357,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2) ) = callref;  
 
@@ -382,7 +382,7 @@
 		return -1;
 	}
 
-        (*skb)->free = 1;
+        SET_SKB_FREE((*skb));
 
         *((ushort*) skb_put(*skb, 2)) = chan->callref;  
 
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/pcbit/drv.c linux/drivers/isdn/pcbit/drv.c
--- v2.1.26/linux/drivers/isdn/pcbit/drv.c	Mon Nov 18 01:31:31 1996
+++ linux/drivers/isdn/pcbit/drv.c	Tue Feb 25 17:12:50 1997
@@ -37,7 +37,6 @@
 #include <linux/isdnif.h>
 #include <asm/string.h>
 #include <asm/io.h>
-#include <asm/uaccess.h>
 
 #include "pcbit.h"
 #include "edss1.h"
@@ -227,7 +226,6 @@
 	struct pcbit_dev  *dev;
 	struct pcbit_chan *chan;
 	struct callb_data info;
-	char *cp;
 
 	dev = finddev(ctl->driver);
 
@@ -246,14 +244,7 @@
 		break;
 	case ISDN_CMD_DIAL:
 		info.type = EV_USR_SETUP_REQ;
-		info.data.setup.CalledPN = (char *) &ctl->num;
-		cp = strchr(info.data.setup.CalledPN, ',');
-		if (cp)
-			*cp = 0;
-		else {
-			printk(KERN_DEBUG "DIAL: error in CalledPN\n");
-			return -1;
-		}		
+		info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone;
 		pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info);
 		break;
 	case ISDN_CMD_ACCEPTD:
@@ -281,7 +272,7 @@
 		pcbit_clear_msn(dev);
 		break;
 	case ISDN_CMD_SETEAZ:
-		pcbit_set_msn(dev, ctl->num);
+		pcbit_set_msn(dev, ctl->parm.num);
 		break;
 	case ISDN_CMD_SETL3:
 		if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS)
@@ -429,8 +420,7 @@
 		{
 			u_char cbuf[1024];
 
-			if (copy_from_user(cbuf, buf, len))
-				return -EFAULT;
+			copy_from_user(cbuf, buf, len);
 			for (i=0; i<len; i++)
 				writeb(cbuf[i], dev->sh_mem + i);
 		}
@@ -448,11 +438,7 @@
 			/* get it into kernel space */
 			if ((ptr = kmalloc(len, GFP_KERNEL))==NULL)
 				return -ENOMEM;
-			if (copy_from_user(ptr, buf, len))
-			{
-				kfree(ptr);
-				return -EFAULT;
-			}
+			copy_from_user(ptr, buf, len);
 			loadbuf = ptr;
 		}
 		else
@@ -751,7 +737,7 @@
 #endif
 	}
 
-	skb->free = 1;
+	SET_SKB_FREE(skb);
 
 	kfree_skb(skb, FREE_READ);
 
@@ -767,12 +753,12 @@
 static int stat_end = 0;
 
 
-extern inline int memcpy_to_COND(int flag, void *d, void *s, int len)
-{
+static __inline void
+memcpy_to_COND(int flag, char *d, const char *s, int len) {
 	if (flag)
-		return copy_to_user(d, s, len);
-	memcpy(d, s, len);
-	return 0;
+		copy_to_user(d, s, len);
+	else
+		memcpy(d, s, len);
 }
 
 
@@ -790,27 +776,24 @@
 
 	if (stat_st < stat_end)
 	{
-		if (memcpy_to_COND(user, buf, statbuf + stat_st, len))
-			return -EFAULT;
+		memcpy_to_COND(user, buf, statbuf + stat_st, len);
 		stat_st += len;	   
 	}
 	else
 	{
 		if (len > STATBUF_LEN - stat_st)
 		{
-			if (memcpy_to_COND(user, buf, statbuf + stat_st, 
-				       STATBUF_LEN - stat_st))
-			       	return -EFAULT;
-			if (memcpy_to_COND(user, buf, statbuf, 
-				       len - (STATBUF_LEN - stat_st)))
-				return -EFAULT;
+			memcpy_to_COND(user, buf, statbuf + stat_st, 
+				       STATBUF_LEN - stat_st);
+			memcpy_to_COND(user, buf, statbuf, 
+				       len - (STATBUF_LEN - stat_st));
+
 			stat_st = len - (STATBUF_LEN - stat_st);
 		}
 		else
 		{
-			if (memcpy_to_COND(user, buf, statbuf + stat_st,
-				       len))
-			       return -EFAULT;
+			memcpy_to_COND(user, buf, statbuf + stat_st, 
+				       len);
 
 			stat_st += len;
 			
@@ -953,7 +936,7 @@
 		return -ENODEV;
 	}
 
-	cmd = (struct pcbit_ioctl *) ctl->num;
+	cmd = (struct pcbit_ioctl *) ctl->parm.num;
 
 	switch(ctl->arg) {
 	case PCBIT_IOCTL_GETSTAT:
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/pcbit/layer2.c linux/drivers/isdn/pcbit/layer2.c
--- v2.1.26/linux/drivers/isdn/pcbit/layer2.c	Sat Jun 29 10:36:22 1996
+++ linux/drivers/isdn/pcbit/layer2.c	Tue Feb 25 17:12:50 1997
@@ -1,13 +1,13 @@
 /*
  * Copyright (C) 1996 Universidade de Lisboa
- * 
+ *
  * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
  *
- * This software may be used and distributed according to the terms of 
+ * This software may be used and distributed according to the terms of
  * the GNU Public License, incorporated herein by reference.
  */
 
-/*        
+/*
  *        PCBIT-D low-layer interface
  */
 
@@ -19,7 +19,7 @@
 /*
  *        TODO: better handling of errors
  *              re-write/remove debug printks
- */ 
+ */
 
 #define __NO_VERSION__
 
@@ -57,7 +57,7 @@
 
 /*
  *  task queue struct
- */ 
+ */
 
 
 
@@ -66,16 +66,16 @@
  *  drv.c
  */
 
-extern void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, 
-			     struct sk_buff * skb,
+extern void pcbit_l3_receive(struct pcbit_dev *dev, ulong msg,
+			     struct sk_buff *skb,
 			     ushort hdr_len, ushort refnum);
 
 /*
  *  Prototypes
  */
 
-void pcbit_deliver(void * data);
-static void pcbit_transmit(struct pcbit_dev * dev);
+void pcbit_deliver(void *data);
+static void pcbit_transmit(struct pcbit_dev *dev);
 
 static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack);
 
@@ -83,9 +83,10 @@
 static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info);
 static void pcbit_l2_err_recover(unsigned long data);
 
-static void pcbit_firmware_bug(struct pcbit_dev * dev);
+static void pcbit_firmware_bug(struct pcbit_dev *dev);
 
-static __inline__ void pcbit_sched_delivery(struct pcbit_dev *dev)
+static __inline__ void
+pcbit_sched_delivery(struct pcbit_dev *dev)
 {
 	queue_task(&dev->qdelivery, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
@@ -96,71 +97,67 @@
  *  Called from layer3
  */
 
-int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum, 
-		   struct sk_buff *skb, unsigned short hdr_len)
-        
-{
-        struct frame_buf * frame, * ptr;
-        unsigned long flags;
-
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
-                dev_kfree_skb(skb, FREE_WRITE);
-                return -1;
-        }
-
-        if ( (frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf), 
-						   GFP_ATOMIC)) == NULL ) {
-                printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
+int
+pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
+	       struct sk_buff *skb, unsigned short hdr_len)
+{
+	struct frame_buf *frame,
+	*ptr;
+	unsigned long flags;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return -1;
+	}
+	if ((frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf),
+						  GFP_ATOMIC)) == NULL) {
+		printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
 		dev_kfree_skb(skb, FREE_WRITE);
-                return -1;
-        }
+		return -1;
+	}
+	frame->msg = msg;
+	frame->refnum = refnum;
+	frame->copied = 0;
+	frame->hdr_len = hdr_len;
+
+	if (skb)
+		frame->dt_len = skb->len - hdr_len;
+	else
+		frame->dt_len = 0;
 
-        frame->msg = msg;
-        frame->refnum = refnum;
-        frame->copied = 0;        
-        frame->hdr_len = hdr_len;
-
-        if (skb) {
-                frame->dt_len = skb->len - hdr_len;
-		if (frame->dt_len == 0)
-                        skb->lock++;
-        }
-        else
-                frame->dt_len = 0;
-
-        frame->skb = skb;
+	frame->skb = skb;
 
-        frame->next = NULL;
+	frame->next = NULL;
 
-        save_flags(flags);
-        cli();
+	save_flags(flags);
+	cli();
+
+	if (dev->write_queue == NULL) {
+		dev->write_queue = frame;
+		restore_flags(flags);
+		pcbit_transmit(dev);
+	} else {
+		for (ptr = dev->write_queue; ptr->next; ptr = ptr->next);
+		ptr->next = frame;
 
-        if (dev->write_queue == NULL) {
-                dev->write_queue = frame;
 		restore_flags(flags);
-                pcbit_transmit(dev);
-        }
-        else {        
-                for(ptr=dev->write_queue; ptr->next; ptr=ptr->next);
-                ptr->next = frame;
-                
-                restore_flags(flags);
-        }
-        return 0;
+	}
+	return 0;
 }
 
-static __inline__ void pcbit_tx_update(struct pcbit_dev *dev, ushort len)
+static __inline__ void
+pcbit_tx_update(struct pcbit_dev *dev, ushort len)
 {
-        u_char info;
+	u_char info;
 
-        dev->send_seq = (dev->send_seq + 1) % 8;
+	dev->send_seq = (dev->send_seq + 1) % 8;
 
-        dev->fsize[dev->send_seq] = len;
-        info = 0;
-        info |= dev->rcv_seq  << 3;
-        info |= dev->send_seq;
+	dev->fsize[dev->send_seq] = len;
+	info = 0;
+	info |= dev->rcv_seq << 3;
+	info |= dev->send_seq;
 
-        writeb(info, dev->sh_mem + BANK4);
+	writeb(info, dev->sh_mem + BANK4);
 
 }
 
@@ -168,46 +165,47 @@
  * called by interrupt service routine or by write_2
  */
 
-static void pcbit_transmit(struct pcbit_dev * dev)
+static void
+pcbit_transmit(struct pcbit_dev *dev)
 {
-        struct frame_buf * frame = NULL;
-        unsigned char unacked;
-        int flen;           /* fragment frame length including all headers */
-	int totlen;         /* non-fragmented frame length */
-        int free;
-	int count, cp_len;
-        unsigned long flags;
+	struct frame_buf *frame = NULL;
+	unsigned char unacked;
+	int flen;               /* fragment frame length including all headers */
+	int totlen;             /* non-fragmented frame length */
+	int free;
+	int count,
+	 cp_len;
+	unsigned long flags;
 	unsigned short tt;
 
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
-                return;
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
 
-        unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07;
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
 
 	save_flags(flags);
 	cli();
 
-        if (dev->free > 16 && dev->write_queue && unacked < 7) {
+	if (dev->free > 16 && dev->write_queue && unacked < 7) {
 
-                if (!dev->w_busy)
-                        dev->w_busy = 1;
-                else
-                {
-                        restore_flags(flags);
-                        return;
-                }
+		if (!dev->w_busy)
+			dev->w_busy = 1;
+		else {
+			restore_flags(flags);
+			return;
+		}
 
 
-                frame = dev->write_queue;
-                free = dev->free;
+		frame = dev->write_queue;
+		free = dev->free;
 
-                restore_flags(flags);
+		restore_flags(flags);
 
 		if (frame->copied == 0) {
 
-                        /* Type 0 frame */
+			/* Type 0 frame */
 
-                        struct msg_fmt * msg;
+			struct msg_fmt *msg;
 
 			if (frame->skb)
 				totlen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len;
@@ -216,67 +214,65 @@
 
 			flen = MIN(totlen, free);
 
-                        msg = (struct msg_fmt *) &(frame->msg);
+			msg = (struct msg_fmt *) &(frame->msg);
+
+			/*
+			 *  Board level 2 header
+			 */
 
-                        /*
-                         *  Board level 2 header 
-                         */
+			pcbit_writew(dev, flen - FRAME_HDR_LEN);
 
-                        pcbit_writew(dev, flen - FRAME_HDR_LEN);
+			pcbit_writeb(dev, msg->cpu);
 
-                        pcbit_writeb(dev, msg->cpu);
+			pcbit_writeb(dev, msg->proc);
 
-                        pcbit_writeb(dev, msg->proc);
+			/* TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
 
-                        /* TH */
-                        pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+			/* TD */
+			pcbit_writew(dev, frame->dt_len);
 
-                        /* TD */
-                        pcbit_writew(dev, frame->dt_len);
 
+			/*
+			 *  Board level 3 fixed-header
+			 */
 
-                        /*
-                         *  Board level 3 fixed-header 
-                         */
-	
-                        /* LEN = TH */
-                        pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
-	
-                        /* XX */
-                        pcbit_writew(dev, 0);
+			/* LEN = TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
 
-                        /* C + S */
-                        pcbit_writeb(dev, msg->cmd);
-                        pcbit_writeb(dev, msg->scmd);
+			/* XX */
+			pcbit_writew(dev, 0);
+
+			/* C + S */
+			pcbit_writeb(dev, msg->cmd);
+			pcbit_writeb(dev, msg->scmd);
+
+			/* NUM */
+			pcbit_writew(dev, frame->refnum);
 
-                        /* NUM */
-                        pcbit_writew(dev, frame->refnum);
-			
 			count = FRAME_HDR_LEN + PREHDR_LEN;
-		}
-		else {
+		} else {
 			/* Type 1 frame */
 
-                        totlen = 2 + (frame->skb->len - frame->copied);
-	  
+			totlen = 2 + (frame->skb->len - frame->copied);
+
 			flen = MIN(totlen, free);
 
-                        /* TT */
-                        tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */
-                        pcbit_writew(dev, tt);
+			/* TT */
+			tt = ((ushort) (flen - 2)) | 0x8000U;	/* Type 1 */
+			pcbit_writew(dev, tt);
 
 			count = 2;
 		}
 
 		if (frame->skb) {
-			cp_len = MIN(frame->skb->len - frame->copied, 
-                                     flen - count);
-		
-			memcpy_topcbit(dev, frame->skb->data + frame->copied, 
-                                       cp_len);
+			cp_len = MIN(frame->skb->len - frame->copied,
+				     flen - count);
+
+			memcpy_topcbit(dev, frame->skb->data + frame->copied,
+				       cp_len);
 			frame->copied += cp_len;
 		}
-
 		/* bookkeeping */
 		dev->free -= flen;
 		pcbit_tx_update(dev, flen);
@@ -285,28 +281,24 @@
 		cli();
 
 
-                if (frame->skb == NULL || frame->copied == frame->skb->len) {
-
-                        dev->write_queue = frame->next;
+		if (frame->skb == NULL || frame->copied == frame->skb->len) {
 
-                        if (frame->skb != NULL) {
-                                /* free frame */
-                                dev_kfree_skb(frame->skb, FREE_WRITE);
-                        }
-
-                        kfree(frame);
-                }
+			dev->write_queue = frame->next;
 
+			if (frame->skb != NULL) {
+				/* free frame */
+				dev_kfree_skb(frame->skb, FREE_WRITE);
+			}
+			kfree(frame);
+		}
 		dev->w_busy = 0;
-		restore_flags(flags);				
-        }
-        else
-	{
 		restore_flags(flags);
-#ifdef DEBUG		
-                printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
-                       unacked, dev->free, dev->write_queue ? "not empty" : 
-		       "empty");	
+	} else {
+		restore_flags(flags);
+#ifdef DEBUG
+		printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
+		     unacked, dev->free, dev->write_queue ? "not empty" :
+		       "empty");
 #endif
 	}
 }
@@ -316,18 +308,18 @@
  *  deliver a queued frame to the upper layer
  */
 
-void pcbit_deliver(void * data)
-{ 
-        struct frame_buf *frame;
-        unsigned long flags;
+void
+pcbit_deliver(void *data)
+{
+	struct frame_buf *frame;
+	unsigned long flags;
 	struct msg_fmt msg;
-        struct pcbit_dev *dev = (struct pcbit_dev *) data;
+	struct pcbit_dev *dev = (struct pcbit_dev *) data;
 
 	save_flags(flags);
-        cli();
+	cli();
 
-	while((frame=dev->read_queue))
-	{
+	while ((frame = dev->read_queue)) {
 		dev->read_queue = frame->next;
 		restore_flags(flags);
 
@@ -336,12 +328,12 @@
 		msg.cmd = frame->skb->data[2];
 		msg.scmd = frame->skb->data[3];
 
-		frame->refnum = *((ushort*) frame->skb->data + 4);
-		frame->msg = *((ulong*) &msg);
-		
+		frame->refnum = *((ushort *) frame->skb->data + 4);
+		frame->msg = *((ulong *) & msg);
+
 		skb_pull(frame->skb, 6);
 
-		pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len, 
+		pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len,
 				 frame->refnum);
 
 		kfree(frame);
@@ -354,110 +346,104 @@
 }
 
 /*
- * Reads BANK 2 & Reassembles 
+ * Reads BANK 2 & Reassembles
  */
 
-static void pcbit_receive(struct pcbit_dev * dev)
+static void
+pcbit_receive(struct pcbit_dev *dev)
 {
-        unsigned short tt;
-	u_char cpu, proc;
-        struct frame_buf * frame = NULL;
-        unsigned long flags;
-        u_char type1;
+	unsigned short tt;
+	u_char cpu,
+	 proc;
+	struct frame_buf *frame = NULL;
+	unsigned long flags;
+	u_char type1;
 
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
-                return;
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
 
-        tt = pcbit_readw(dev);
+	tt = pcbit_readw(dev);
 
-        if ((tt & 0x7fffU) > 511) {
-                printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n", 
+	if ((tt & 0x7fffU) > 511) {
+		printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n",
 		       tt);
-                pcbit_l2_error(dev);
-                return;
-        }
-
-        if (!(tt & 0x8000U))
-        {                       /* Type 0 */
-                type1 = 0;
+		pcbit_l2_error(dev);
+		return;
+	}
+	if (!(tt & 0x8000U)) {  /* Type 0 */
+		type1 = 0;
 
-                if (dev->read_frame) {
-                        printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
+		if (dev->read_frame) {
+			printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
 #if 0
-                        pcbit_l2_error(dev);
-                        return;
+			pcbit_l2_error(dev);
+			return;
 #else
 			/* discard previous queued frame */
 			if (dev->read_frame->skb) {
-				dev->read_frame->skb->free = 1;
+				SET_SKB_FREE(dev->read_frame->skb);
 				kfree_skb(dev->read_frame->skb, FREE_READ);
 			}
 			kfree(dev->read_frame);
 			dev->read_frame = NULL;
 #endif
-                }
+		}
+		frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC);
+
+		if (frame == NULL) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+		memset(frame, 0, sizeof(struct frame_buf));
+
+		cpu = pcbit_readb(dev);
+		proc = pcbit_readb(dev);
 
-                frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC);
-      
-                if (frame == NULL) {
-                        printk(KERN_WARNING "kmalloc failed\n");
-                        return;
-                }
-                memset(frame, 0, sizeof(struct frame_buf));
-      
-                cpu = pcbit_readb(dev);
-                proc = pcbit_readb(dev);
-
-
-                if (cpu != 0x06 && cpu != 0x02)
-                {
-                        printk (KERN_DEBUG "pcbit: invalid cpu value\n");
+
+		if (cpu != 0x06 && cpu != 0x02) {
+			printk(KERN_DEBUG "pcbit: invalid cpu value\n");
 			kfree(frame);
 			pcbit_l2_error(dev);
-                        return;
-                }
-
-                /*
-                 * we discard cpu & proc on receiving
-                 * but we read it to update the pointer
-                 */
-      
-                frame->hdr_len = pcbit_readw(dev);
-                frame->dt_len = pcbit_readw(dev);
-
-		/* 
-                 * 0 sized packet 
-                 * I don't know if they are an error or not...
-                 * But they are very frequent
-                 * Not documented
-                 */
+			return;
+		}
+		/*
+		 * we discard cpu & proc on receiving
+		 * but we read it to update the pointer
+		 */
+
+		frame->hdr_len = pcbit_readw(dev);
+		frame->dt_len = pcbit_readw(dev);
+
+		/*
+		   * 0 sized packet
+		   * I don't know if they are an error or not...
+		   * But they are very frequent
+		   * Not documented
+		 */
 
 		if (frame->hdr_len == 0) {
-                        kfree(frame);
+			kfree(frame);
 #ifdef DEBUG
-                        printk(KERN_DEBUG "0 sized frame\n");
+			printk(KERN_DEBUG "0 sized frame\n");
 #endif
 			pcbit_firmware_bug(dev);
-                        return;
+			return;
 		}
-		
-                /* sanity check the length values */
-                if (frame->hdr_len > 1024 || frame->dt_len > 2048)
-                {
+		/* sanity check the length values */
+		if (frame->hdr_len > 1024 || frame->dt_len > 2048) {
 #ifdef DEBUG
-                        printk(KERN_DEBUG "length problem: ");
-                        printk(KERN_DEBUG "TH=%04x TD=%04x\n", 
-			       frame->hdr_len, 
-                               frame->dt_len);
+			printk(KERN_DEBUG "length problem: ");
+			printk(KERN_DEBUG "TH=%04x TD=%04x\n",
+			       frame->hdr_len,
+			       frame->dt_len);
 #endif
 			pcbit_l2_error(dev);
 			kfree(frame);
-                        return;
-                }
-
-                /* minimum frame read */
+			return;
+		}
+		/* minimum frame read */
 
-		frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len + 
+		frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len +
 					   ((frame->hdr_len + 15) & ~15));
 
 		if (!frame->skb) {
@@ -465,62 +451,57 @@
 			kfree(frame);
 			return;
 		}
-
-                /* 16 byte alignment for IP */		       
+		/* 16 byte alignment for IP */
 		if (frame->dt_len)
-                        skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
+			skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
 
-        }
-        else {
+	} else {
 		/* Type 1 */
-                type1 = 1;
-                tt &= 0x7fffU;
+		type1 = 1;
+		tt &= 0x7fffU;
 
-                if (!(frame = dev->read_frame)) {                
-                        printk("Type 1 frame and no frame queued\n");
+		if (!(frame = dev->read_frame)) {
+			printk("Type 1 frame and no frame queued\n");
 #if 1
 			/* usually after an error: toss frame */
 			dev->readptr += tt;
 			if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN)
 				dev->readptr -= BANKLEN;
 #else
-                        pcbit_l2_error(dev);
+			pcbit_l2_error(dev);
 #endif
-                        return;
+			return;
 
-                }
-        }
+		}
+	}
 
 	memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt);
 
 	frame->copied += tt;
 
-        if (frame->copied == frame->hdr_len + frame->dt_len) {
-           
-                save_flags(flags);
-                cli();
-      
-                if (type1) {
-                        dev->read_frame = NULL;
-                }
-      
-                if (dev->read_queue) {
-                        struct frame_buf *ptr;
-                        for(ptr=dev->read_queue;ptr->next;ptr=ptr->next);
-                        ptr->next = frame;
-                }
-                else
-                        dev->read_queue = frame;
-                
-                restore_flags(flags);
-
-        }
-        else {        
-                save_flags(flags);
-                cli();
-                dev->read_frame = frame;
-                restore_flags(flags);
-        }
+	if (frame->copied == frame->hdr_len + frame->dt_len) {
+
+		save_flags(flags);
+		cli();
+
+		if (type1) {
+			dev->read_frame = NULL;
+		}
+		if (dev->read_queue) {
+			struct frame_buf *ptr;
+			for (ptr = dev->read_queue; ptr->next; ptr = ptr->next);
+			ptr->next = frame;
+		} else
+			dev->read_queue = frame;
+
+		restore_flags(flags);
+
+	} else {
+		save_flags(flags);
+		cli();
+		dev->read_frame = frame;
+		restore_flags(flags);
+	}
 }
 
 /*
@@ -529,194 +510,173 @@
  *  gotta send a fake acknowledgment to the upper layer somehow
  */
 
-static __inline__ void pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan * chan)
+static __inline__ void
+pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan)
 {
-     isdn_ctrl ictl;
+	isdn_ctrl ictl;
 
-     if (chan->queued) {
-	     chan->queued--;
-	     
-	     ictl.driver = dev->id;
-	     ictl.command = ISDN_STAT_BSENT;
-	     ictl.arg = chan->id;
-	     dev->dev_if->statcallb(&ictl);     
-     }
+	if (chan->queued) {
+		chan->queued--;
+
+		ictl.driver = dev->id;
+		ictl.command = ISDN_STAT_BSENT;
+		ictl.arg = chan->id;
+		dev->dev_if->statcallb(&ictl);
+	}
 }
 
-static void pcbit_firmware_bug(struct pcbit_dev * dev) 
+static void
+pcbit_firmware_bug(struct pcbit_dev *dev)
 {
-        struct pcbit_chan *chan;
-        
-        chan = dev->b1;
+	struct pcbit_chan *chan;
 
-        if (chan->fsm_state == ST_ACTIVE) {
-	    pcbit_fake_conf(dev, chan);
-        }
+	chan = dev->b1;
 
-        chan = dev->b2;
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
+	chan = dev->b2;
 
-        if (chan->fsm_state == ST_ACTIVE) {
-	    pcbit_fake_conf(dev, chan);
-        }
- 
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
 }
 
-void pcbit_irq_handler(int interrupt, void * devptr, struct pt_regs *regs)
+void
+pcbit_irq_handler(int interrupt, void *devptr, struct pt_regs *regs)
 {
-        struct pcbit_dev * dev;
-        u_char info, ack_seq, read_seq;
+	struct pcbit_dev *dev;
+	u_char info,
+	 ack_seq,
+	 read_seq;
 
 	dev = (struct pcbit_dev *) devptr;
 
-        if (!dev)
-        {
-                printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
-                return;
-        }
-
+	if (!dev) {
+		printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
+		return;
+	}
 	if (dev->interrupt) {
-	        printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
+		printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
 		return;
 	}
-
 	dev->interrupt = 1;
 
-        info = readb(dev->sh_mem + BANK3);
+	info = readb(dev->sh_mem + BANK3);
 
-        if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR)
-        {
-                pcbit_l2_active_conf(dev, info);
+	if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) {
+		pcbit_l2_active_conf(dev, info);
 		dev->interrupt = 0;
-                return;
-        }
-
-        if (info & 0x40U)       /* E bit set */
-        {
+		return;
+	}
+	if (info & 0x40U) {     /* E bit set */
 #ifdef DEBUG
 		printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n");
 #endif
-                pcbit_l2_error(dev);
+		pcbit_l2_error(dev);
 		dev->interrupt = 0;
-                return;
-        }
-
-        if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) 
-	{
-	        dev->interrupt = 0;
-                return;
+		return;
 	}
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev->interrupt = 0;
+		return;
+	}
+	ack_seq = (info >> 3) & 0x07U;
+	read_seq = (info & 0x07U);
 
-        ack_seq =  (info >> 3) & 0x07U;
-        read_seq = (info & 0x07U); 
-        
 	dev->interrupt = 0;
 
-        if (read_seq != dev->rcv_seq)
-        {
-		while (read_seq != dev->rcv_seq)
-		{
+	if (read_seq != dev->rcv_seq) {
+		while (read_seq != dev->rcv_seq) {
 			pcbit_receive(dev);
 			dev->rcv_seq = (dev->rcv_seq + 1) % 8;
 		}
 		pcbit_sched_delivery(dev);
-        }
-
-	if (ack_seq != dev->unack_seq)
-        {
-                pcbit_recv_ack(dev, ack_seq);
-        }
-
-
+	}
+	if (ack_seq != dev->unack_seq) {
+		pcbit_recv_ack(dev, ack_seq);
+	}
 	info = dev->rcv_seq << 3;
 	info |= dev->send_seq;
-    
-	writeb(info, dev->sh_mem + BANK4);  
+
+	writeb(info, dev->sh_mem + BANK4);
 }
 
 
-static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
+static void
+pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
 {
-        u_char state;
+	u_char state;
 
-        state = dev->l2_state;
+	state = dev->l2_state;
 
 #ifdef DEBUG
-        printk(KERN_DEBUG "layer2_active_confirm\n");
+	printk(KERN_DEBUG "layer2_active_confirm\n");
 #endif
-        
 
-        if (info & 0x80U ) {
-                dev->rcv_seq = info & 0x07U;
-                dev->l2_state = L2_RUNNING;
-        }
-        else
-                dev->l2_state = L2_DOWN;
-                
-	if (state == L2_STARTING)
-                wake_up_interruptible(&dev->set_running_wq);
 
-        if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
-                pcbit_transmit(dev);
-        }
+	if (info & 0x80U) {
+		dev->rcv_seq = info & 0x07U;
+		dev->l2_state = L2_RUNNING;
+	} else
+		dev->l2_state = L2_DOWN;
+
+	if (state == L2_STARTING)
+		wake_up_interruptible(&dev->set_running_wq);
 
+	if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
+		pcbit_transmit(dev);
+	}
 }
 
-static void pcbit_l2_err_recover(unsigned long data) 
+static void
+pcbit_l2_err_recover(unsigned long data)
 {
 
-        struct pcbit_dev * dev;
-        struct frame_buf *frame;
+	struct pcbit_dev *dev;
+	struct frame_buf *frame;
 
-        dev = (struct pcbit_dev *) data;
+	dev = (struct pcbit_dev *) data;
 
-        del_timer(&dev->error_recover_timer);
-	if (dev->w_busy || dev->r_busy)
-	{
+	del_timer(&dev->error_recover_timer);
+	if (dev->w_busy || dev->r_busy) {
 		init_timer(&dev->error_recover_timer);
 		dev->error_recover_timer.expires = jiffies + ERRTIME;
 		add_timer(&dev->error_recover_timer);
 		return;
 	}
-
 	dev->w_busy = dev->r_busy = 1;
 
-        if (dev->read_frame)
-	{
-                if (dev->read_frame->skb)
-		{
-			dev->read_frame->skb->free = 1; 
-                        kfree_skb(dev->read_frame->skb, FREE_READ);
-		}
-                kfree(dev->read_frame);
-                dev->read_frame = NULL;
-        }
-
-
-        if (dev->write_queue)
-	{
-                frame = dev->write_queue;
+	if (dev->read_frame) {
+		if (dev->read_frame->skb) {
+			SET_SKB_FREE(dev->read_frame->skb);
+			kfree_skb(dev->read_frame->skb, FREE_READ);
+		}
+		kfree(dev->read_frame);
+		dev->read_frame = NULL;
+	}
+	if (dev->write_queue) {
+		frame = dev->write_queue;
 #ifdef FREE_ON_ERROR
-                dev->write_queue = dev->write_queue->next;
+		dev->write_queue = dev->write_queue->next;
 
 		if (frame->skb) {
-                        dev_kfree_skb(frame->skb, FREE_WRITE);
+			dev_kfree_skb(frame->skb, FREE_WRITE);
 		}
-		
-                kfree(frame);
-#else		
-                frame->copied = 0;
+		kfree(frame);
+#else
+		frame->copied = 0;
 #endif
-        }
-
-        dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
-        dev->free = 511;
-        dev->l2_state = L2_ERROR;
+	}
+	dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
+	dev->free = 511;
+	dev->l2_state = L2_ERROR;
 
 	/* this is an hack... */
 	pcbit_firmware_bug(dev);
 
-        dev->writeptr = dev->sh_mem;
-        dev->readptr = dev->sh_mem + BANK2;
+	dev->writeptr = dev->sh_mem;
+	dev->readptr = dev->sh_mem + BANK2;
 
 	writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
 	       dev->sh_mem + BANK4);
@@ -724,24 +684,25 @@
 
 }
 
-static void pcbit_l2_error(struct pcbit_dev *dev)
+static void
+pcbit_l2_error(struct pcbit_dev *dev)
 {
-        if (dev->l2_state == L2_RUNNING) {
+	if (dev->l2_state == L2_RUNNING) {
 
-                printk(KERN_INFO "pcbit: layer 2 error\n");
+		printk(KERN_INFO "pcbit: layer 2 error\n");
 
 #ifdef DEBUG
-                log_state(dev);
+		log_state(dev);
 #endif
-                
-                dev->l2_state = L2_DOWN;
 
-                init_timer(&dev->error_recover_timer);
-                dev->error_recover_timer.function = &pcbit_l2_err_recover;
-                dev->error_recover_timer.data = (ulong) dev;
-                dev->error_recover_timer.expires = jiffies + ERRTIME;
-                add_timer(&dev->error_recover_timer);
-        }
+		dev->l2_state = L2_DOWN;
+
+		init_timer(&dev->error_recover_timer);
+		dev->error_recover_timer.function = &pcbit_l2_err_recover;
+		dev->error_recover_timer.data = (ulong) dev;
+		dev->error_recover_timer.expires = jiffies + ERRTIME;
+		add_timer(&dev->error_recover_timer);
+	}
 }
 
 /*
@@ -751,58 +712,52 @@
  *   call pcbit_transmit to write possible queued frames
  */
 
-static void  pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
+static void
+pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
 {
-        int i, count;
-        int unacked;
+	int i,
+	 count;
+	int unacked;
 
-        unacked = (dev->send_seq + (8 - dev->unack_seq) ) & 0x07;
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
 
-        /* dev->unack_seq < ack <= dev->send_seq; */
+	/* dev->unack_seq < ack <= dev->send_seq; */
 
-        if (unacked) 
-	{
+	if (unacked) {
 
 		if (dev->send_seq > dev->unack_seq)
-                        if (ack <= dev->unack_seq || ack > dev->send_seq)
-			{
-                                printk(KERN_DEBUG
-				       "layer 2 ack unacceptable - dev %d",
+			if (ack <= dev->unack_seq || ack > dev->send_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
 				       dev->id);
 
-                                pcbit_l2_error(dev);
-                        }
-                        else
-                                if (ack > dev->send_seq && ack <= dev->unack_seq)
-				{
-					printk(KERN_DEBUG
-					       "layer 2 ack unacceptable - dev %d",
-					       dev->id);
-                                        pcbit_l2_error(dev);
-                                }
-
-                /* ack is acceptable */
-
-
-                i = dev->unack_seq;
-
-                do {
-                        dev->unack_seq = i = (i + 1) % 8;
-                        dev->free += dev->fsize[i];
-                } while (i != ack);
-
-                count = 0;
-                while (count < 7 && dev->write_queue) 
-		{
+				pcbit_l2_error(dev);
+			} else if (ack > dev->send_seq && ack <= dev->unack_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
+				       dev->id);
+				pcbit_l2_error(dev);
+			}
+		/* ack is acceptable */
+
+
+		i = dev->unack_seq;
+
+		do {
+			dev->unack_seq = i = (i + 1) % 8;
+			dev->free += dev->fsize[i];
+		} while (i != ack);
+
+		count = 0;
+		while (count < 7 && dev->write_queue) {
 			u8 lsend_seq = dev->send_seq;
 
-                        pcbit_transmit(dev);
+			pcbit_transmit(dev);
 
 			if (dev->send_seq == lsend_seq)
 				break;
-                        count++;
-                }    
-        }
-        else
-                printk(KERN_DEBUG "recv_ack: unacked = 0\n");
+			count++;
+		}
+	} else
+		printk(KERN_DEBUG "recv_ack: unacked = 0\n");
 }
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/pcbit/module.c linux/drivers/isdn/pcbit/module.c
--- v2.1.26/linux/drivers/isdn/pcbit/module.c	Fri Dec 27 02:03:22 1996
+++ linux/drivers/isdn/pcbit/module.c	Tue Feb 25 17:12:50 1997
@@ -1,13 +1,13 @@
 /*
  * Copyright (C) 1996 Universidade de Lisboa
- *
+ * 
  * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
  *
- * This software may be used and distributed according to the terms of
+ * This software may be used and distributed according to the terms of 
  * the GNU Public License, incorporated herein by reference.
  */
 
-/*
+/*        
  *        PCBIT-D module support
  */
 
@@ -35,7 +35,10 @@
 extern int pcbit_init_dev(int board, int mem_base, int irq);
 
 #ifdef MODULE
-EXPORT_NO_SYMBOLS;
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_PARM(mem, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_PCBIT_CARDS) "i");
+#endif
 #define pcbit_init init_module
 #endif
 
@@ -45,11 +48,11 @@
 
 	num_boards = 0;
 
-	printk(KERN_INFO
+	printk(KERN_INFO 
 	       "PCBIT-D device driver v 0.5 - "
 	       "Copyright (C) 1996 Universidade de Lisboa\n");
 
-	if (mem[0] || irq[0])
+	if (mem[0] || irq[0]) 
 	{
 		for (board=0; board < MAX_PCBIT_CARDS && mem[board] && irq[board]; board++)
 		{
@@ -57,14 +60,14 @@
 				mem[board] = 0xD0000;
 			if (!irq[board])
 				irq[board] = 5;
-
+			
 			if (pcbit_init_dev(board, mem[board], irq[board]) == 0)
 				num_boards++;
-
-			else
+		
+			else 
 			{
-				printk(KERN_WARNING
-				       "pcbit_init failed for dev %d",
+				printk(KERN_WARNING 
+				       "pcbit_init failed for dev %d", 
 				       board + 1);
 				return -EIO;
 			}
@@ -75,7 +78,7 @@
 
 	if (!num_boards)
 	{
-		printk(KERN_INFO
+		printk(KERN_INFO 
 		       "Trying to detect board using default settings\n");
 		if (pcbit_init_dev(0, 0xD0000, 5) == 0)
 			num_boards++;
@@ -83,6 +86,13 @@
 			return -EIO;
 	}
 
+	/* No symbols to export, hide all symbols */
+#if (LINUX_VERSION_CODE < 0x020111)
+	register_symtab(NULL);
+#else
+	EXPORT_NO_SYMBOLS;
+#endif
+
 	return 0;
 }
 
@@ -93,7 +103,7 @@
 
 	for (board = 0; board < num_boards; board++)
 		pcbit_terminate(board);
-	printk(KERN_INFO
+	printk(KERN_INFO 
 	       "PCBIT-D module unloaded\n");
 }
 
@@ -112,7 +122,7 @@
 			mem[i]	= ints[j];
 			j++; argc--;
 		}
-
+		
 		if (argc) {
 			irq[i]	= ints[j];
 			j++; argc--;
@@ -122,3 +132,6 @@
 	}
 }
 #endif
+
+
+
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/Makefile linux/drivers/isdn/sc/Makefile
--- v2.1.26/linux/drivers/isdn/sc/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/Makefile	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,44 @@
+#
+#  $Id: Makefile.kernel,v 1.1 1996/11/07 13:07:40 fritz Exp $
+#  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#  For more information, please contact gpl-info@spellcast.com or write:
+#
+#     SpellCaster Telecommunications Inc.
+#     5621 Finch Avenue East, Unit #3
+#     Scarborough, Ontario  Canada
+#     M1B 2T9
+#     +1 (416) 297-8565
+#     +1 (416) 297-6433 Facsimile
+#
+
+L_OBJS :=
+M_OBJS :=
+O_OBJS := shmem.o init.o debug.o packet.o command.o event.o \
+	ioctl.o interrupt.o message.o timer.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_SC),y)
+	O_TARGET += sc.o
+else
+  ifeq ($(CONFIG_ISDN_DRV_SC),m)
+    O_TARGET += sc.o
+    M_OBJS += sc.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/card.h linux/drivers/isdn/sc/card.h
--- v2.1.26/linux/drivers/isdn/sc/card.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/card.h	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,110 @@
+/*
+ *  $Id: card.h,v 1.1 1996/11/07 13:07:40 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  card.h - Driver parameters for SpellCaster ISA ISDN adapters
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#ifndef CARD_H
+#define CARD_H
+
+/*
+ * We need these if they're not already included
+ */
+#include <linux/timer.h>
+#include <linux/isdnif.h>
+#include "message.h"
+
+/*
+ * Amount of time to wait for a reset to complete
+ */
+#define CHECKRESET_TIME		milliseconds(4000)
+
+/*
+ * Amount of time between line status checks
+ */
+#define CHECKSTAT_TIME		milliseconds(8000)
+
+/*
+ * The maximum amount of time to wait for a message response
+ * to arrive. Use exclusively by send_and_receive
+ */
+#define SAR_TIMEOUT		milliseconds(10000)
+
+/*
+ * Macro to determine is a card id is valid
+ */
+#define IS_VALID_CARD(x)	((x >= 0) && (x <= cinst))
+
+/*
+ * Per channel status and configuration
+ */
+typedef struct {
+	int l2_proto;
+	int l3_proto;
+	char dn[50];
+	unsigned long first_sendbuf;	/* Offset of first send buffer */
+	unsigned int num_sendbufs;	/* Number of send buffers */
+	unsigned int free_sendbufs;	/* Number of free sendbufs */
+	unsigned int next_sendbuf;	/* Next sequential buffer */
+	char eazlist[50];		/* Set with SETEAZ */
+	char sillist[50];		/* Set with SETSIL */
+	int eazclear;			/* Don't accept calls if TRUE */
+} bchan;
+
+/*
+ * Everything you want to know about the adapter ...
+ */
+typedef struct {
+	int model;
+	int driverId;			/* LL Id */
+	char devicename[20];		/* The device name */
+	isdn_if *card;			/* ISDN4Linux structure */
+	bchan *channel;			/* status of the B channels */
+	char nChannels;			/* Number of channels */
+	unsigned int interrupt;		/* Interrupt number */
+	int iobase;			/* I/O Base address */
+	int ioport[MAX_IO_REGS];	/* Index to I/O ports */
+	int shmem_pgport;		/* port for the exp mem page reg. */
+	int shmem_magic;		/* adapter magic number */
+	unsigned int rambase;		/* Shared RAM base address */
+	unsigned int ramsize;		/* Size of shared memory */
+	RspMessage async_msg;		/* Async response message */
+	int want_async_messages;	/* Snoop the Q ? */
+	unsigned char seq_no;		/* Next send seq. number */
+	struct timer_list reset_timer;	/* Check reset timer */
+	struct timer_list stat_timer;	/* Check startproc timer */
+	unsigned char nphystat;		/* Latest PhyStat info */
+	unsigned char phystat;		/* Last PhyStat info */
+	HWConfig_pl hwconfig;		/* Hardware config info */
+	char load_ver[11];		/* CommManage Version string */
+	char proc_ver[11];		/* CommEngine Version */
+	int StartOnReset;		/* Indicates startproc after reset */
+	int EngineUp;			/* Indicates CommEngine Up */
+	int trace_mode;			/* Indicate if tracing is on */
+} board;
+
+#endif /* CARD_H */
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/command.c linux/drivers/isdn/sc/command.c
--- v2.1.26/linux/drivers/isdn/sc/command.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/command.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,566 @@
+/*
+ *  $Id: command.c,v 1.3 1997/02/11 22:53:40 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"		/* This must be first */
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+int dial(int card, unsigned long channel, setup_parm setup);
+int hangup(int card, unsigned long channel);
+int answer(int card, unsigned long channel);
+int clreaz(int card, unsigned long channel);
+int seteaz(int card, unsigned long channel, char *);
+int geteaz(int card, unsigned long channel, char *);
+int setsil(int card, unsigned long channel, char *);
+int getsil(int card, unsigned long channel, char *);
+int setl2(int card, unsigned long arg);
+int getl2(int card, unsigned long arg);
+int setl3(int card, unsigned long arg);
+int getl3(int card, unsigned long arg);
+int lock(void);
+int unlock(void);
+int acceptb(int card, unsigned long channel);
+
+extern int cinst;
+extern board *adapter[];
+
+extern int sc_ioctl(int, scs_ioctl *);
+extern int setup_buffers(int, int, unsigned int);
+extern int indicate_status(int, int,ulong,char*);
+extern void check_reset(unsigned long);
+extern int send_and_receive(int, unsigned int, unsigned char, unsigned char,
+                unsigned char, unsigned char, unsigned char, unsigned char *,
+                RspMessage *, int);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+                unsigned int, unsigned int, unsigned int, unsigned int *);
+extern inline void pullphone(char *, char *);
+
+#ifdef DEBUG
+/*
+ * Translate command codes to strings
+ */
+static char *commands[] = { "ISDN_CMD_IOCTL",
+			    "ISDN_CMD_DIAL",
+			    "ISDN_CMD_ACCEPTB",
+			    "ISDN_CMD_ACCEPTB",
+			    "ISDN_CMD_HANGUP",
+			    "ISDN_CMD_CLREAZ",
+			    "ISDN_CMD_SETEAZ",
+			    "ISDN_CMD_GETEAZ",
+			    "ISDN_CMD_SETSIL",
+			    "ISDN_CMD_GETSIL",
+			    "ISDN_CMD_SETL2",
+			    "ISDN_CMD_GETL2",
+			    "ISDN_CMD_SETL3",
+			    "ISDN_CMD_GETL3",
+			    "ISDN_CMD_LOCK",
+			    "ISDN_CMD_UNLOCK",
+			    "ISDN_CMD_SUSPEND",
+			    "ISDN_CMD_RESUME" };
+
+/*
+ * Translates ISDN4Linux protocol codes to strings for debug messages
+ */
+static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" };
+static char *l2protos[] = { "ISDN_PROTO_L2_X75I",
+			    "ISDN_PROTO_L2_X75UI",
+			    "ISDN_PROTO_L2_X75BUI",
+			    "ISDN_PROTO_L2_HDLC",
+			    "ISDN_PROTO_L2_TRANS" };
+#endif
+
+int get_card_from_id(int driver)
+{
+	int i;
+
+	for(i = 0 ; i < cinst ; i++) {
+		if(adapter[i]->driverId == driver)
+			return i;
+	}
+	return -NODEV;
+}
+
+/* 
+ * command
+ */
+
+int command(isdn_ctrl *cmd)
+{
+	int card;
+
+	card = get_card_from_id(cmd->driver);
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Received %s command from Link Layer\n",
+		adapter[card]->devicename, commands[cmd->command]);
+
+	/*
+	 * Dispatch the command
+	 */
+	switch(cmd->command) {
+	case ISDN_CMD_IOCTL:
+	{
+		unsigned long 	cmdptr;
+		scs_ioctl	ioc;
+		int		err;
+
+		memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
+		if((err = verify_area(VERIFY_READ, 
+			(scs_ioctl *) cmdptr, sizeof(scs_ioctl)))) {
+			pr_debug("%s: Failed to verify user space 0x%x\n",
+				adapter[card]->devicename, cmdptr);
+			return err;
+
+		}
+		copy_from_user(&ioc, (scs_ioctl *) cmdptr, 
+			sizeof(scs_ioctl));
+		return sc_ioctl(card, &ioc);
+	}
+	case ISDN_CMD_DIAL:
+		return dial(card, cmd->arg, cmd->parm.setup);
+	case ISDN_CMD_HANGUP:
+		return hangup(card, cmd->arg);
+	case ISDN_CMD_ACCEPTD:
+		return answer(card, cmd->arg);
+	case ISDN_CMD_ACCEPTB:
+		return acceptb(card, cmd->arg);
+	case ISDN_CMD_CLREAZ:
+		return clreaz(card, cmd->arg);
+	case ISDN_CMD_SETEAZ:
+		return seteaz(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_GETEAZ:
+		return geteaz(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_SETSIL:
+		return setsil(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_GETSIL:
+		return getsil(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_SETL2:
+		return setl2(card, cmd->arg);
+	case ISDN_CMD_GETL2:
+		return getl2(card, cmd->arg);
+	case ISDN_CMD_SETL3:
+		return setl3(card, cmd->arg);
+	case ISDN_CMD_GETL3:
+		return getl3(card, cmd->arg);
+	case ISDN_CMD_LOCK:
+		return lock();
+	case ISDN_CMD_UNLOCK:
+		return unlock();
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * Confirm our ability to communicate with the board.  This test assumes no
+ * other message activity is present
+ */
+int loopback(int card) 
+{
+
+	int status;
+	static char testmsg[] = "Test Message";
+	RspMessage rspmsg;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Sending loopback message\n", adapter[card]->devicename);
+	
+
+	/*
+	 * Send the loopback message to confirm that memory transfer is
+	 * operational
+	 */
+	status = send_and_receive(card, CMPID, cmReqType1,
+				  cmReqClass0,
+				  cmReqMsgLpbk,
+				  0,
+				  (unsigned char) strlen(testmsg),
+				  (unsigned char *)testmsg,
+				  &rspmsg, SAR_TIMEOUT);
+
+
+	if (!status) {
+		pr_debug("%s: Loopback message successfully sent\n",
+			adapter[card]->devicename);
+		if(strcmp(rspmsg.msg_data.byte_array, testmsg)) {
+			pr_debug("%s: Loopback return != sent\n",
+				adapter[card]->devicename);
+			return -EIO;
+		}
+		return 0;
+	}
+	else {
+		pr_debug("%s: Send loopback message failed\n",
+			adapter[card]->devicename);
+		return -EIO;
+	}
+
+}
+
+/*
+ * start the onboard firmware
+ */
+int startproc(int card) 
+{
+	int status;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*
+	 * send start msg 
+	 */
+       	status = sendmessage(card, CMPID,cmReqType2,
+			  cmReqClass0,
+			  cmReqStartProc,
+			  0,0,0);
+	pr_debug("%s: Sent startProc\n", adapter[card]->devicename);
+	
+	return status;
+}
+
+
+int loadproc(int card, char *data) 
+{
+	return -1;
+}
+
+
+/*
+ * Dials the number passed in 
+ */
+int dial(int card, unsigned long channel, setup_parm setup) 
+{
+	int status;
+	char Phone[48];
+  
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*extract ISDN number to dial from eaz/msn string*/ 
+	strcpy(Phone,setup.phone); 
+
+	/*send the connection message*/
+	status = sendmessage(card, CEPID,ceReqTypePhy,
+				ceReqClass1,
+				ceReqPhyConnect,
+				(unsigned char) channel+1, 
+				strlen(Phone),
+				(unsigned int *) Phone);
+
+	pr_debug("%s: Dialing %s on channel %d\n",
+		adapter[card]->devicename, Phone, channel+1);
+	
+	return status;
+}
+
+/*
+ * Answer an incoming call 
+ */
+int answer(int card, unsigned long channel) 
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	if(setup_buffers(card, channel+1, BUFFER_SIZE)) {
+		hangup(card, channel+1);
+		return -ENOBUFS;
+	}
+
+	indicate_status(card, ISDN_STAT_BCONN,channel,NULL);
+	pr_debug("%s: Answered incoming call on channel %s\n",
+		adapter[card]->devicename, channel+1);
+	return 0;
+}
+
+/*
+ * Hangup up the call on specified channel
+ */
+int hangup(int card, unsigned long channel) 
+{
+	int status;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	status = sendmessage(card, CEPID, ceReqTypePhy,
+						 ceReqClass1,
+						 ceReqPhyDisconnect,
+						 (unsigned char) channel+1,
+						 0,
+						 NULL);
+	pr_debug("%s: Sent HANGUP message to channel %d\n",
+		adapter[card]->devicename, channel+1);
+	return status;
+}
+
+/*
+ * Set the layer 2 protocol (X.25, HDLC, Raw)
+ */
+int setl2(int card, unsigned long arg) 
+{
+	int status =0;
+	int protocol,channel;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+	protocol = arg >> 8;
+	channel = arg & 0xff;
+	adapter[card]->channel[channel].l2_proto = protocol;
+	pr_debug("%s: Level 2 protocol for channel %d set to %s from %d\n",
+		adapter[card]->devicename, channel+1,l2protos[adapter[card]->channel[channel].l2_proto],protocol);
+
+	/*
+	 * check that the adapter is also set to the correct protocol
+	 */
+	pr_debug("%s: Sending GetFrameFormat for channel %d\n",
+		adapter[card]->devicename, channel+1);
+	status = sendmessage(card, CEPID, ceReqTypeCall,
+ 				ceReqClass0,
+ 				ceReqCallGetFrameFormat,
+ 				(unsigned char)channel+1,
+ 				1,
+ 				(unsigned int *) protocol);
+	if(status) 
+		return status;
+	return 0;
+}
+
+/*
+ * Get the layer 2 protocol
+ */
+int getl2(int card, unsigned long channel) {
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Level 2 protocol for channel %d reported as %s\n",
+		adapter[card]->devicename, channel+1,
+		l2protos[adapter[card]->channel[channel].l2_proto]);
+
+	return adapter[card]->channel[channel].l2_proto;
+}
+
+/*
+ * Set the layer 3 protocol
+ */
+int setl3(int card, unsigned long channel) 
+{
+	int protocol = channel >> 8;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	adapter[card]->channel[channel].l3_proto = protocol;
+	pr_debug("%s: Level 3 protocol for channel %d set to %s\n",
+		adapter[card]->devicename, channel+1, l3protos[protocol]);
+	return 0;
+}
+
+/*
+ * Get the layer 3 protocol
+ */
+int getl3(int card, unsigned long arg) 
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Level 3 protocol for channel %d reported as %s\n",
+			adapter[card]->devicename, arg+1,
+			l3protos[adapter[card]->channel[arg].l3_proto]);
+	return adapter[card]->channel[arg].l3_proto;
+}
+
+
+int acceptb(int card, unsigned long channel)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	if(setup_buffers(card, channel+1, BUFFER_SIZE))
+	{
+		hangup(card, channel+1);
+		return -ENOBUFS;
+	}
+
+	pr_debug("%s: B-Channel connection accepted on channel %d\n",
+		adapter[card]->devicename, channel+1);
+	indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
+	return 0;
+}
+
+int clreaz(int card, unsigned long arg)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(adapter[card]->channel[arg].eazlist, "");
+	adapter[card]->channel[arg].eazclear = 1;
+	pr_debug("%s: EAZ List cleared for channel %d\n",
+		adapter[card]->devicename, arg+1);
+	return 0;
+}
+
+int seteaz(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(adapter[card]->channel[arg].eazlist, num);
+	adapter[card]->channel[arg].eazclear = 0;
+	pr_debug("%s: EAZ list for channel %d set to: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].eazlist);
+	return 0;
+}
+
+int geteaz(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(num, adapter[card]->channel[arg].eazlist);
+	pr_debug("%s: EAZ List for channel %d reported: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].eazlist);
+	return 0;
+}
+
+int setsil(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(adapter[card]->channel[arg].sillist, num);
+	pr_debug("%s: Service Indicators for channel %d set: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].sillist);
+	return 0;
+}
+
+int getsil(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(num, adapter[card]->channel[arg].sillist);
+	pr_debug("%s: SIL for channel %d reported: %s\n",
+		adapter[card]->devicename, arg+1,
+		adapter[card]->channel[arg].sillist);
+	return 0;
+}
+
+
+int lock()
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+int unlock()
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int reset(int card)
+{
+	unsigned long flags;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+
+	if(adapter[card]->EngineUp) {
+		del_timer(&adapter[card]->stat_timer);	
+	}
+
+	adapter[card]->EngineUp = 0;
+
+	save_flags(flags);
+	cli();
+	init_timer(&adapter[card]->reset_timer);
+	adapter[card]->reset_timer.function = check_reset;
+	adapter[card]->reset_timer.data = card;
+	adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
+	add_timer(&adapter[card]->reset_timer);
+	restore_flags(flags);
+
+	outb(0x1,adapter[card]->ioport[SFT_RESET]); 
+
+	pr_debug("%s: Adapter Reset\n", adapter[card]->devicename);
+	return 0;
+}
+
+void flushreadfifo (int card)
+{
+	while(inb(adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA)
+		inb(adapter[card]->ioport[FIFO_READ]);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/debug.c linux/drivers/isdn/sc/debug.c
--- v2.1.26/linux/drivers/isdn/sc/debug.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/debug.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,80 @@
+/*
+ *  $Id: debug.c,v 1.2 1996/11/20 17:49:50 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+#include <linux/kernel.h>
+
+#define NULL	0x0
+
+#if LINUX_VERSION_CODE < 66363	/* Linux 1.3.59 there was a change to interrupts */
+	#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d)
+	#define FREE_IRQ(a,b) free_irq(a)
+#else
+	#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+	#define FREE_IRQ(a,b) free_irq(a,b)
+#endif
+
+inline char *strcpy(char *, const char *);
+
+int dbg_level = 0;
+static char dbg_funcname[255];
+
+void dbg_endfunc(void)
+{
+	if (dbg_level) {
+		printk("<-- Leaving function %s\n", dbg_funcname);
+		strcpy(dbg_funcname, "");
+	}
+}
+
+void dbg_func(char *func)
+{
+	strcpy(dbg_funcname, func);
+	if(dbg_level)
+		printk("--> Entering function %s\n", dbg_funcname);
+}
+
+inline char *strcpy(char *dest, const char *src)
+{
+	char *i = dest;
+	char *j = (char *) src;
+
+	while(*j) {
+		*i = *j;
+		i++; j++;
+	}
+	*(++i) = NULL;
+	return dest;
+}
+
+inline void pullphone(char *dn, char *str)
+{
+	int i = 0;
+
+	while(dn[i] != ',')
+		str[i] = dn[i++];
+	str[i] = 0x0;
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/debug.h linux/drivers/isdn/sc/debug.h
--- v2.1.26/linux/drivers/isdn/sc/debug.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/debug.h	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,35 @@
+/*
+ *  $Id: debug.h,v 1.1 1996/11/07 13:07:42 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#if LINUX_VERSION_CODE < 131072
+	#error You cant use this driver on kernels older than 2.0
+#else
+	#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+	#define FREE_IRQ(a,b) free_irq(a,b)
+#endif
+
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/event.c linux/drivers/isdn/sc/event.c
--- v2.1.26/linux/drivers/isdn/sc/event.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/event.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,75 @@
+/*
+ *  $Id: event.c,v 1.3 1997/02/11 22:53:41 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern int cinst;
+extern board *adapter[];
+
+#ifdef DEBUG
+static char *events[] = { "ISDN_STAT_STAVAIL",
+			  "ISDN_STAT_ICALL",
+			  "ISDN_STAT_RUN",
+			  "ISDN_STAT_STOP",
+			  "ISDN_STAT_DCONN",
+			  "ISDN_STAT_BCONN",
+			  "ISDN_STAT_DHUP",
+			  "ISDN_STAT_BHUP",
+			  "ISDN_STAT_CINF",
+			  "ISDN_STAT_LOAD",
+			  "ISDN_STAT_UNLOAD",
+			  "ISDN_STAT_BSENT",
+			  "ISDN_STAT_NODCH",
+			  "ISDN_STAT_ADDCH",
+			  "ISDN_STAT_CAUSE" };
+#endif
+
+int indicate_status(int card, int event,ulong Channel,char *Data)
+{
+	isdn_ctrl cmd;
+
+	pr_debug("%s: Indicating event %s on Channel %d\n",
+		adapter[card]->devicename, events[event-256], Channel);
+	if (Data != NULL){
+		pr_debug("%s: Event data: %s\n", adapter[card]->devicename,
+			Data);
+		if (event == ISDN_STAT_ICALL)
+			memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
+		else
+			strcpy(cmd.parm.num, Data);
+	}
+
+	cmd.command = event;
+	cmd.driver = adapter[card]->driverId;
+	cmd.arg = Channel;
+	return adapter[card]->card->statcallb(&cmd);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/hardware.h linux/drivers/isdn/sc/hardware.h
--- v2.1.26/linux/drivers/isdn/sc/hardware.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/hardware.h	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,117 @@
+/*
+ * Hardware specific macros, defines and structures
+ */
+
+#ifndef HARDWARE_H
+#define HARDWARE_H
+
+#include <asm/param.h>			/* For HZ */
+
+/*
+ * General hardware parameters common to all ISA adapters
+ */
+
+#define MAX_CARDS	4		/* The maximum number of cards to
+					   control or probe for. If you change
+					   this, you must also change the number
+					   of elements in io, irq, and ram to
+					   match. Initialized in init.c */
+/*
+extern unsigned int io[];
+extern unsigned char irq[];
+extern unsigned long ram[];
+*/
+
+#define SIGNATURE	0x87654321	/* Board reset signature */
+#define SIG_OFFSET	0x1004		/* Where to find signature in shared RAM */
+#define TRACE_OFFSET	0x1008		/* Trace enable word offset in shared RAM */
+#define BUFFER_OFFSET	0x1800		/* Beginning of buffers */
+
+/* I/O Port parameters */
+#define IOBASE_MIN	0x180		/* Lowest I/O port address */
+#define IOBASE_MAX	0x3C0		/* Highest I/O port address */
+#define IOBASE_OFFSET	0x20		/* Inter-board I/O port gap used during
+					   probing */
+#define FIFORD_OFFSET	0x0
+#define FIFOWR_OFFSET	0x400
+#define FIFOSTAT_OFFSET	0x1000
+#define RESET_OFFSET	0x2800
+#define PG0_OFFSET	0x3000		/* Offset from I/O Base for Page 0 register */
+#define PG1_OFFSET	0x3400		/* Offset from I/O Base for Page 1 register */
+#define PG2_OFFSET	0x3800		/* Offset from I/O Base for Page 2 register */
+#define PG3_OFFSET	0x3C00		/* Offset from I/O Base for Page 3 register */
+
+#define FIFO_READ	0		/* FIFO Read register */
+#define FIFO_WRITE	1		/* FIFO Write rgister */
+#define LO_ADDR_PTR	2		/* Extended RAM Low Addr Pointer */
+#define HI_ADDR_PTR	3		/* Extended RAM High Addr Pointer */
+#define NOT_USED_1	4
+#define FIFO_STATUS	5		/* FIFO Status Register */
+#define NOT_USED_2	6
+#define MEM_OFFSET	7
+#define SFT_RESET	10		/* Reset Register */
+#define EXP_BASE	11		/* Shared RAM Base address */
+#define EXP_PAGE0	12		/* Shared RAM Page0 register */
+#define EXP_PAGE1	13		/* Shared RAM Page1 register */
+#define EXP_PAGE2	14		/* Shared RAM Page2 register */
+#define EXP_PAGE3	15		/* Shared RAM Page3 register */
+#define IRQ_SELECT	16		/* IRQ selection register */
+#define MAX_IO_REGS	17		/* Total number of I/O ports */
+
+/* FIFO register values */
+#define RF_HAS_DATA	0x01		/* fifo has data */
+#define RF_QUART_FULL	0x02		/* fifo quarter full */
+#define RF_HALF_FULL	0x04		/* fifo half full */
+#define RF_NOT_FULL	0x08		/* fifo not full */
+#define WF_HAS_DATA	0x10		/* fifo has data */
+#define WF_QUART_FULL	0x20		/* fifo quarter full */
+#define WF_HALF_FULL	0x40		/* fifo half full */
+#define WF_NOT_FULL	0x80		/* fifo not full */
+
+/* Shared RAM parameters */
+#define SRAM_MIN	0xC0000         /* Lowest host shared RAM address */
+#define SRAM_MAX	0xEFFFF         /* Highest host shared RAM address */
+#define SRAM_PAGESIZE	0x4000		/* Size of one RAM page (16K) */
+
+/* Shared RAM buffer parameters */
+#define BUFFER_SIZE	0x800		/* The size of a buffer in bytes */
+#define BUFFER_BASE	BUFFER_OFFSET	/* Offset from start of shared RAM
+					   where buffer start */
+#define BUFFERS_MAX	16		/* Maximum number of send/receive
+					   buffers per channel */
+#define HDLC_PROTO	0x01		/* Frame Format for Layer 2 */
+
+#define BRI_BOARD	0
+#define POTS_BOARD	1
+#define PRI_BOARD	2
+
+/*
+ * Specific hardware parameters for the DataCommute/BRI
+ */
+#define BRI_CHANNELS	2		/* Number of B channels */
+#define BRI_BASEPG_VAL	0x98
+#define BRI_MAGIC	0x60000		/* Magic Number */
+#define BRI_MEMSIZE	0x10000		/* Ammount of RAM (64K) */
+#define BRI_PARTNO	"72-029"
+#define BRI_FEATURES	ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+/*
+ * Specific hardware parameters for the DataCommute/PRI
+ */
+#define PRI_CHANNELS	23		/* Number of B channels */
+#define PRI_BASEPG_VAL	0x88
+#define PRI_MAGIC	0x20000		/* Magic Number */
+#define PRI_MEMSIZE	0x100000	/* Amount of RAM (1M) */
+#define PRI_PARTNO	"72-030"
+#define PRI_FEATURES	ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+
+/*
+ * Some handy macros
+ */
+
+/* Return the number of jiffies in a given number of msecs */
+#define milliseconds(x)	(x/(1000/HZ))
+
+/* Determine if a channel number is valid for the adapter */
+#define IS_VALID_CHANNEL(y,x)	((x>0) && (x <= adapter[y]->channels))
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/includes.h linux/drivers/isdn/sc/includes.h
--- v2.1.26/linux/drivers/isdn/sc/includes.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/includes.h	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,15 @@
+#include <linux/module.h>
+#include <linux/version.h>
+#include <stdio.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include "debug.h"
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/init.c linux/drivers/isdn/sc/init.c
--- v2.1.26/linux/drivers/isdn/sc/init.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/init.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,599 @@
+#include "includes.h"
+#include "hardware.h"
+#include "card.h"
+
+board *adapter[MAX_CARDS];
+int cinst;
+
+static char devname[] = "scX";
+const char version[] = "2.0b1";
+
+const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
+
+/* insmod set parameters */
+unsigned int io[] = {0,0,0,0};
+unsigned char irq[] = {0,0,0,0};
+unsigned long ram[] = {0,0,0,0};
+int do_reset = 0;
+
+static int sup_irq[] = { 11, 10, 9, 5, 12, 14, 7, 3, 4, 6 };
+#define MAX_IRQS	10
+
+extern void interrupt_handler(int, void *, struct pt_regs *);
+extern int sndpkt(int, int, struct sk_buff *);
+extern int command(isdn_ctrl *);
+extern int indicate_status(int, int, ulong, char*);
+extern int reset(int);
+
+int identify_board(unsigned long, unsigned int);
+
+int irq_supported(int irq_x)
+{
+	int i;
+	for(i=0 ; i < MAX_IRQS ; i++) {
+		if(sup_irq[i] == irq_x)
+			return 1;
+	}
+	return 0;
+}
+
+#ifdef MODULE
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_PARM(io, "1-4i");
+MODULE_PARM(irq, "1-4i");
+MODULE_PARM(ram, "1-4i");
+MODULE_PARM(do_reset, "i");
+#endif
+#define init_sc init_module
+#else
+/*
+Initialization code for non-module version to be included
+
+void sc_setup(char *str, int *ints)
+{
+}
+*/
+#endif
+
+int init_sc(void)
+{
+	int b = -1;
+	int i, j;
+	int status = -ENODEV;
+
+	unsigned long memsize = 0;
+	unsigned long features = 0;
+	isdn_if *interface;
+	unsigned char channels;
+	unsigned char pgport;
+	unsigned long magic;
+	int model;
+	int last_base = IOBASE_MIN;
+	int probe_exhasted = 0;
+
+#ifdef MODULE
+	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
+#else
+	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
+#endif
+	pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
+
+	while(b++ < MAX_CARDS - 1) {
+		pr_debug("Probing for adapter #%d\n", b);
+		/*
+		 * Initialize reusable variables
+		 */
+		model = -1;
+		magic = 0;
+		channels = 0;
+		pgport = 0;
+
+		/* 
+		 * See if we should probe for IO base 
+		 */
+		pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
+			io[b] == 0 ? "will" : "won't");
+		if(io[b]) {
+			/*
+			 * No, I/O Base has been provided
+			 */
+			for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+				if(check_region(io[b] + i * 0x400, 1)) {
+					pr_debug("check_region for 0x%x failed\n", io[b] + i * 0x400);
+					io[b] = 0;
+					break;
+				}
+			}
+
+			/*
+			 * Confirm the I/O Address with a test
+			 */
+			if(io[b] == 0) {
+				pr_debug("I/O Address 0x%x is in use.\n");
+				continue;
+			}
+
+			outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+			if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
+				pr_debug("I/O Base 0x%x fails test\n");
+				continue;
+			}
+		}
+		else {
+			/*
+			 * Yes, probe for I/O Base
+			 */
+			if(probe_exhasted) {
+				pr_debug("All probe addresses exhasted, skipping\n");
+				continue;
+			}
+			pr_debug("Probing for I/O...\n");
+			for (i = last_base ; i <= IOBASE_MAX ; i += IOBASE_OFFSET) {
+				int found_io = 1;
+				if (i == IOBASE_MAX) {
+					probe_exhasted = 1; /* No more addresses to probe */
+					pr_debug("End of Probes\n");
+				}
+				last_base = i + IOBASE_OFFSET;
+				pr_debug("  checking 0x%x...", i);
+				for ( j = 0 ; j < MAX_IO_REGS - 1 ; j++) {
+					if(check_region(i + j * 0x400, 1)) {
+						pr_debug("Failed\n");
+						found_io = 0;
+						break;
+					}
+				}	
+
+				if(found_io) {
+					io[b] = i;
+					outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+					if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { 
+						pr_debug("Failed by test\n");
+						continue;
+					}
+					pr_debug("Passed\n");
+					break;
+				}
+			}
+			if(probe_exhasted) {
+				continue;
+			}
+		}
+
+		/*
+		 * See if we should probe for shared RAM
+		 */
+		if(do_reset) {
+			pr_debug("Doing a SAFE probe reset\n");
+			outb(0xFF, io[b] + RESET_OFFSET);
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + milliseconds(10000);
+			schedule();
+		}
+		pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b],
+			ram[b] == 0 ? "will" : "won't");
+
+		if(ram[b]) {
+			/*
+			 * No, the RAM base has been provided
+			 * Just look for a signature and ID the
+			 * board model
+			 */
+			if(!check_region(ram[b], SRAM_PAGESIZE)) {
+				pr_debug("check_region for RAM base 0x%x succeeded\n", ram[b]);
+			 	model = identify_board(ram[b], io[b]);
+			}
+		}
+		else {
+			/*
+			 * Yes, probe for free RAM and look for
+			 * a signature and id the board model
+			 */
+			for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) {
+				pr_debug("Checking RAM address 0x%x...\n", i);
+				if(!check_region(i, SRAM_PAGESIZE)) {
+					pr_debug("  check_region succeeded\n");
+					model = identify_board(i, io[b]);
+					if (model >= 0) {
+						pr_debug("  Identified a %s\n",
+							boardname[model]);
+						ram[b] = i;
+						break;
+					}
+					pr_debug("  Unidentifed or inaccessible\n");
+					continue;
+				}
+				pr_debug("  check_region failed\n");
+			}
+		}
+		/*
+		 * See if we found free RAM and the board model
+		 */
+		if(!ram[b] || model < 0) {
+			/*
+			 * Nope, there was no place in RAM for the
+			 * board, or it couldn't be identified
+			 */
+			 pr_debug("Failed to find an adapter at 0x%x\n", ram[b]);
+			 continue;
+		}
+
+		/*
+		 * Set the board's magic number, memory size and page register
+		 */
+		switch(model) {
+		case PRI_BOARD:
+			channels = 23;
+			magic = 0x20000;
+			memsize = 0x100000;
+			features = PRI_FEATURES;
+			break;
+
+		case BRI_BOARD:
+		case POTS_BOARD:
+			channels = 2;
+			magic = 0x60000;
+			memsize = 0x10000;
+			features = BRI_FEATURES;
+			break;
+		}
+		switch(ram[b] >> 12 & 0x0F) {
+		case 0x0:
+			pr_debug("RAM Page register set to EXP_PAGE0\n");
+			pgport = EXP_PAGE0;
+			break;
+
+		case 0x4:
+			pr_debug("RAM Page register set to EXP_PAGE1\n");
+			pgport = EXP_PAGE1;
+			break;
+
+		case 0x8:
+			pr_debug("RAM Page register set to EXP_PAGE2\n");
+			pgport = EXP_PAGE2;
+			break;
+
+		case 0xC:
+			pr_debug("RAM Page register set to EXP_PAGE3\n");
+			pgport = EXP_PAGE3;
+			break;
+
+		default:
+			pr_debug("RAM base address doesn't fall on 16K boundary\n");
+			continue;
+		}
+
+		pr_debug("current IRQ: %d  b: %d\n",irq[b],b);
+		/*
+		 * See if we should probe for an irq
+		 */
+		if(irq[b]) {
+			/*
+			 * No we were given one
+			 * See that it is supported and free
+			 */
+			pr_debug("Trying for IRQ: %d\n",irq[b]);
+			if (irq_supported(irq[b])) {
+				if(REQUEST_IRQ(irq[b], interrupt_handler, 
+					SA_PROBE, "sc_probe", NULL)) {
+					pr_debug("IRQ %d is already in use\n", 
+						irq[b]);
+					continue;
+				}
+				FREE_IRQ(irq[b], NULL);
+			}
+		}
+		else {
+			/*
+			 * Yes, we need to probe for an IRQ
+			 */
+			pr_debug("Probing for IRQ...\n");
+			for (i = 0; i < MAX_IRQS ; i++) {
+				if(!REQUEST_IRQ(sup_irq[i], interrupt_handler, SA_PROBE, "sc_probe", NULL)) {
+					pr_debug("Probed for and found IRQ %d\n", sup_irq[i]);
+					FREE_IRQ(sup_irq[i], NULL);
+					irq[b] = sup_irq[i];
+					break;
+				}
+			}
+		}
+
+		/*
+		 * Make sure we got an IRQ
+		 */
+		if(!irq[b]) {
+			/*
+			 * No interrupt could be used
+			 */
+			pr_debug("Failed to aquire an IRQ line\n");
+			continue;
+		}
+
+		/*
+		 * Horray! We found a board, Make sure we can register
+		 * it with ISDN4Linux
+		 */
+		interface = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+		if (interface == NULL) {
+			/*
+			 * Oops, can't malloc isdn_if
+			 */
+			continue;
+		}
+		memset(interface, 0, sizeof(isdn_if));
+
+		interface->hl_hdrlen = 0;
+		interface->channels = channels;
+		interface->maxbufsize = BUFFER_SIZE;
+		interface->features = features;
+		interface->writebuf_skb = sndpkt;
+		interface->writecmd = NULL;
+		interface->command = command;
+		strcpy(interface->id, devname);
+		interface->id[2] = '0' + cinst;
+
+		/*
+		 * Allocate the board structure
+		 */
+		adapter[cinst] = kmalloc(sizeof(board), GFP_KERNEL);
+		if (adapter[cinst] == NULL) {
+			/*
+			 * Oops, can't alloc memory for the board
+			 */
+			kfree(interface);
+			continue;
+		}
+		memset(adapter[cinst], 0, sizeof(board));
+
+		if(!register_isdn(interface)) {
+			/*
+			 * Oops, couldn't register for some reason
+			 */
+			kfree(interface);
+			kfree(adapter[cinst]);
+			continue;
+		}
+
+		adapter[cinst]->card = interface;
+		adapter[cinst]->driverId = interface->channels;
+		strcpy(adapter[cinst]->devicename, interface->id);
+		adapter[cinst]->nChannels = channels;
+		adapter[cinst]->ramsize = memsize;
+		adapter[cinst]->shmem_magic = magic;
+		adapter[cinst]->shmem_pgport = pgport;
+		adapter[cinst]->StartOnReset = 1;
+
+		/*
+		 * Allocate channels status structures
+		 */
+		adapter[cinst]->channel = kmalloc(sizeof(bchan) * channels, GFP_KERNEL);
+		if (adapter[cinst]->channel == NULL) {
+			/*
+			 * Oops, can't alloc memory for the channels
+			 */
+			indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL);	/* Fix me */
+			kfree(interface);
+			kfree(adapter[cinst]);
+			continue;
+		}
+		memset(adapter[cinst]->channel, 0, sizeof(bchan) * channels);
+
+		/*
+		 * Lock down the hardware resources
+		 */
+		adapter[cinst]->interrupt = irq[b];
+		REQUEST_IRQ(adapter[cinst]->interrupt, interrupt_handler, SA_INTERRUPT, 
+			interface->id, NULL);
+		adapter[cinst]->iobase = io[b];
+		for(i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+			adapter[cinst]->ioport[i] = io[b] + i * 0x400;
+			request_region(adapter[cinst]->ioport[i], 1, interface->id);
+			pr_debug("Requesting I/O Port %#x\n", adapter[cinst]->ioport[i]);
+		}
+		adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
+		request_region(adapter[cinst]->ioport[IRQ_SELECT], 1, interface->id);
+		pr_debug("Requesting I/O Port %#x\n", adapter[cinst]->ioport[IRQ_SELECT]);
+		adapter[cinst]->rambase = ram[b];
+		request_region(adapter[cinst]->rambase, SRAM_PAGESIZE, interface->id);
+
+		pr_info("  %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n", 
+			adapter[cinst]->devicename, adapter[cinst]->driverId, 
+			boardname[model], channels, irq[b], io[b], ram[b]);
+		
+		/*
+		 * reset the adapter to put things in motion
+		 */
+		reset(cinst);
+
+		cinst++;
+		status = 0;
+	}
+	if (status) 
+		pr_info("Failed to find any adapters, driver unloaded\n");
+	return status;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	int i, j;
+
+	for(i = 0 ; i < cinst ; i++) {
+		pr_debug("Cleaning up after adapter %d\n", i);
+		/*
+		 * kill the timers
+		 */
+		del_timer(&(adapter[i]->reset_timer));
+		del_timer(&(adapter[i]->stat_timer));
+
+		/*
+		 * Tell I4L we're toast
+		 */
+		indicate_status(i, ISDN_STAT_STOP, 0, NULL);
+		indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
+
+		/*
+		 * Release shared RAM
+		 */
+		release_region(adapter[i]->rambase, SRAM_PAGESIZE);
+
+		/*
+		 * Release the IRQ
+		 */
+		FREE_IRQ(adapter[i]->interrupt, NULL);
+
+		/*
+		 * Reset for a clean start
+		 */
+		outb(0xFF, adapter[i]->ioport[SFT_RESET]);
+
+		/*
+		 * Release the I/O Port regions
+		 */
+		for(j = 0 ; j < MAX_IO_REGS - 1; j++) {
+			release_region(adapter[i]->ioport[j], 1);
+			pr_debug("Releasing I/O Port %#x\n", adapter[i]->ioport[j]);
+		}
+		release_region(adapter[i]->ioport[IRQ_SELECT], 1);
+		pr_debug("Releasing I/O Port %#x\n", adapter[i]->ioport[IRQ_SELECT]);
+
+		/*
+		 * Release any memory we alloced
+		 */
+		kfree(adapter[i]->channel);
+		kfree(adapter[i]->card);
+		kfree(adapter[i]);
+	}
+	pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
+}
+#endif
+
+int identify_board(unsigned long rambase, unsigned int iobase) 
+{
+	unsigned int pgport;
+	unsigned long sig;
+	DualPortMemory *dpm;
+	RspMessage rcvmsg;
+	ReqMessage sndmsg;
+	HWConfig_pl hwci;
+	int x;
+
+	pr_debug("Attempting to identify adapter @ 0x%x io 0x%x\n",
+		rambase, iobase);
+
+	/*
+	 * Enable the base pointer
+	 */
+	outb(rambase >> 12, iobase + 0x2c00);
+
+	switch(rambase >> 12 & 0x0F) {
+	case 0x0:
+		pgport = iobase + PG0_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
+		break;
+		
+	case 0x4:
+		pgport = iobase + PG1_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
+		break;
+
+	case 0x8:
+		pgport = iobase + PG2_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
+		break;
+
+	case 0xC:
+		pgport = iobase + PG3_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
+		break;
+	default:
+		pr_debug("Invalid rambase 0x%lx\n", rambase);
+		return -1;
+	}
+
+	/*
+	 * Try to identify a PRI card
+	 */
+	outb(PRI_BASEPG_VAL, pgport);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + HZ;
+	schedule();
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+#if 0
+/*
+ * For Gary: 
+ * If it's a timing problem, it should be gone with the above schedule()
+ * Another possible reason may be the missing volatile in the original
+ * code. readl() does this for us.
+ */
+	printk("");	/* Hack! Doesn't work without this !!!??? */
+#endif
+	if(sig == SIGNATURE)
+		return PRI_BOARD;
+
+	/*
+	 * Try to identify a PRI card
+	 */
+	outb(BRI_BASEPG_VAL, pgport);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + HZ;
+	schedule();
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+#if 0
+	printk("");	/* Hack! Doesn't work without this !!!??? */
+#endif
+	if(sig == SIGNATURE)
+		return BRI_BOARD;
+
+	return -1;
+
+	/*
+	 * Try to spot a card
+	 */
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+	if(sig != SIGNATURE)
+		return -1;
+
+	dpm = (DualPortMemory *) rambase;
+
+	memset(&sndmsg, 0, MSG_LEN);
+	sndmsg.msg_byte_cnt = 3;
+	sndmsg.type = cmReqType1;
+	sndmsg.class = cmReqClass0;
+	sndmsg.code = cmReqHWConfig;
+	memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
+	outb(0, iobase + 0x400);
+	pr_debug("Sent HWConfig message\n");
+	/*
+	 * Wait for the response
+	 */
+	x = 0;
+	while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1;
+		schedule();
+		x++;
+	}
+	if(x == 100) {
+		pr_debug("Timeout waiting for response\n");
+		return -1;
+	}
+
+	memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
+	pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
+	memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
+	pr_debug("Hardware Config: Interface: %s, RAM Size: %d, Serial: %s\n"
+		 "                 Part: %s, Rev: %s\n",
+		 hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
+		 hwci.serial_no, hwci.part_no, hwci.rev_no);
+
+	if(!strncmp(PRI_PARTNO, hwci.part_no, 6))
+		return PRI_BOARD;
+	if(!strncmp(BRI_PARTNO, hwci.part_no, 6))
+		return BRI_BOARD;
+		
+	return -1;
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c
--- v2.1.26/linux/drivers/isdn/sc/interrupt.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/interrupt.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,258 @@
+/*
+ *  $Id: interrupt.c,v 1.3 1997/02/11 22:53:43 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern indicate_status(int, int, ulong, char *);
+extern void check_phystat(unsigned long);
+extern void dump_messages(int);
+extern int receivemessage(int, RspMessage *);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+        unsigned int, unsigned int, unsigned int, unsigned int *);
+extern void rcvpkt(int, RspMessage *);
+
+extern int cinst;
+extern board *adapter[];
+
+int get_card_from_irq(int irq)
+{
+	int i;
+
+	for(i = 0 ; i < cinst ; i++) {
+		if(adapter[i]->interrupt == irq)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * 
+ */
+void interrupt_handler(int interrupt, void * cardptr, struct pt_regs *regs ) {
+
+	RspMessage rcvmsg;
+	int channel;
+	int card;
+
+	card = get_card_from_irq(interrupt);
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	pr_debug("%s: Entered Interrupt handler\n", adapter[card]->devicename);
+	
+ 	/*
+	 * Pull all of the waiting messages off the response queue
+	 */
+	while (!receivemessage(card, &rcvmsg)) {
+		/*
+		 * Push the message to the adapter structure for
+		 * send_and_receive to snoop
+		 */
+		if(adapter[card]->want_async_messages)
+			memcpy(&(adapter[card]->async_msg), &rcvmsg, sizeof(RspMessage));
+
+		channel = (unsigned int) rcvmsg.phy_link_no;
+		
+		/*
+		 * Trap Invalid request messages
+		 */
+		if(IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) {
+			pr_debug("%s: Invalid request Message, rsp_status = %d\n", 
+				adapter[card]->devicename, rcvmsg.rsp_status);
+			break;	
+		}
+		
+		/*
+		 * Check for a linkRead message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
+		{
+			pr_debug("%s: Received packet 0x%x bytes long at 0x%x\n",
+						adapter[card]->devicename,
+						rcvmsg.msg_data.response.msg_len,
+						rcvmsg.msg_data.response.buff_offset);
+			rcvpkt(card, &rcvmsg);
+			continue;
+
+		}
+
+		/*
+		 * Handle a write acknoledgement
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) {
+			pr_debug("%s: Packet Send ACK on channel %d\n", adapter[card]->devicename,
+				rcvmsg.phy_link_no);
+			adapter[card]->channel[rcvmsg.phy_link_no-1].free_sendbufs++;
+			continue;
+		}
+
+		/*
+		 * Handle a connection message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect)) 
+		{
+			unsigned int callid;
+			setup_parm setup;	
+			pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n",
+						adapter[card]->devicename,
+						rcvmsg.phy_link_no,
+						rcvmsg.rsp_status,
+						rcvmsg.msg_data.byte_array[2]);
+			
+			memcpy(&callid,rcvmsg.msg_data.byte_array,sizeof(int));
+			if(callid>=0x8000 && callid<=0xFFFF)
+			{		
+				pr_debug("%s: Got Dial-Out Rsp\n", adapter[card]->devicename);	
+				indicate_status(card, ISDN_STAT_DCONN,
+						(unsigned long)rcvmsg.phy_link_no-1,NULL);
+				
+			}
+			else if(callid>=0x0000 && callid<=0x7FFF)
+			{
+				pr_debug("%s: Got Incomming Call\n", adapter[card]->devicename);	
+				strcpy(setup.phone,&(rcvmsg.msg_data.byte_array[4]));
+				strcpy(setup.eazmsn,adapter[card]->channel[rcvmsg.phy_link_no-1].dn);
+				setup.si1 = 7;
+				setup.si2 = 0;
+				setup.plan = 0;
+				setup.screen = 0;
+
+				indicate_status(card, ISDN_STAT_ICALL,(unsigned long)rcvmsg.phy_link_no-1,(char *)&setup);
+				indicate_status(card, ISDN_STAT_DCONN,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			}
+			continue;
+		}
+
+		/*
+		 * Handle a disconnection message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect)) 
+		{
+			pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n",
+						adapter[card]->devicename,
+						rcvmsg.phy_link_no,
+						rcvmsg.rsp_status,
+					 	rcvmsg.msg_data.byte_array[2]);
+
+			indicate_status(card, ISDN_STAT_BHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			indicate_status(card, ISDN_STAT_DHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			continue;
+
+		}
+
+		/*
+		 * Handle a startProc engine up message
+		 */
+		if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) {
+			pr_debug("%s: Received EngineUp message\n", adapter[card]->devicename);
+			adapter[card]->EngineUp = 1;
+			sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,1,0,NULL);
+			sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,2,0,NULL);
+			init_timer(&adapter[card]->stat_timer);
+			adapter[card]->stat_timer.function = check_phystat;
+			adapter[card]->stat_timer.data = card;
+			adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
+			add_timer(&adapter[card]->stat_timer);
+			continue;
+		}
+
+		/*
+		 * Start proc response
+		 */
+		if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) {
+			pr_debug("%s: StartProc Response Status %d\n", adapter[card]->devicename,
+				rcvmsg.rsp_status);
+			continue;
+		}
+
+		/*
+		 * Handle a GetMyNumber Rsp
+		 */
+		if (IS_CE_MESSAGE(rcvmsg,Call,0,GetMyNumber)){
+			strcpy(adapter[card]->channel[rcvmsg.phy_link_no-1].dn,rcvmsg.msg_data.byte_array);
+			continue;
+		}
+			
+		/*
+		 * PhyStatus response
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) {
+			unsigned int b1stat, b2stat;
+
+			/*
+			 * Covert the message data to the adapter->phystat code
+			 */
+			b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0];
+			b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1];
+
+			adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */
+			pr_debug("%s: PhyStat is 0x%2x\n", adapter[card]->devicename,
+				adapter[card]->nphystat);
+			continue;
+		}
+
+
+		/* 
+		 * Handle a GetFramFormat
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) {
+			if(rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) {
+				unsigned int proto = HDLC_PROTO;
+				/*
+				 * Set board format to HDLC if it wasn't already
+				 */
+				pr_debug("%s: current frame format: 0x%x, will change to HDLC\n",
+						adapter[card]->devicename,
+					rcvmsg.msg_data.byte_array[0]);
+				sendmessage(card, CEPID, ceReqTypeCall,
+						ceReqClass0,
+						ceReqCallSetFrameFormat,
+						(unsigned char) channel +1,
+						1,&proto);
+				}
+			continue;
+		}
+
+		/*
+		 * Hmm...
+		 */
+		pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n",
+			adapter[card]->devicename, rcvmsg.type, rcvmsg.class, rcvmsg.code, 
+			rcvmsg.phy_link_no);
+
+	}	/* while */
+
+	pr_debug("%s: Exiting Interrupt Handler\n", adapter[card]->devicename);
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/ioctl.c linux/drivers/isdn/sc/ioctl.c
--- v2.1.26/linux/drivers/isdn/sc/ioctl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/ioctl.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,525 @@
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+extern int indicate_status(int, int, unsigned long, char *);
+extern int startproc(int);
+extern int loadproc(int, char *record);
+extern int reset(int);
+extern int send_and_receive(int, unsigned int, unsigned char,unsigned char,
+		unsigned char,unsigned char, 
+		unsigned char, unsigned char *, RspMessage *, int);
+
+extern board *adapter[];
+
+#if 0
+static char *ChannelStates[] = { "Idle", "Ready", "Connecting", "Connected", "Disconnecting" };
+#endif
+
+int GetStatus(int card, boardInfo *);
+
+/*
+ * Process private IOCTL messages (typically from scctrl)
+ */
+int sc_ioctl(int card, scs_ioctl *data)
+{
+	switch(data->command) {
+	case SCIOCRESET:	/* Perform a hard reset of the adapter */
+	{
+		pr_debug("%s: SCIOCRESET: ioctl received\n", adapter[card]->devicename);
+		adapter[card]->StartOnReset = 0;
+		return (reset(card));
+	}
+
+	case SCIOCLOAD:
+	{
+		RspMessage	rcvmsg;
+		char		srec[SCIOC_SRECSIZE];
+		int		status, err;
+
+		pr_debug("%s: SCIOLOAD: ioctl received\n", adapter[card]->devicename);
+		if(adapter[card]->EngineUp) {
+			pr_debug("%s: SCIOCLOAD: Command Failed, LoadProc while engine running.\n",
+				adapter[card]->devicename);
+			return -1;
+		}
+
+		/*
+		 * Get the SRec from user space
+		 */
+		if((err = verify_area(VERIFY_READ, (char *) data->dataptr, sizeof(srec))))
+			return err;
+		copy_from_user(srec, (char *) data->dataptr, sizeof(srec));
+
+		status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
+				0, sizeof(srec), srec, &rcvmsg, SAR_TIMEOUT);
+		if(status) {
+			pr_debug("%s: SCIOCLOAD: Command Failed, status = %d\n", 
+				adapter[card]->devicename, status);
+			return -1;
+		}
+		else {
+			pr_debug("%s: SCIOCLOAD: Command Sucessful\n", adapter[card]->devicename);
+			return 0;
+		}
+	}
+
+	case SCIOCSTART:
+	{
+		pr_debug("%s: SCIOSTART: ioctl received\n", adapter[card]->devicename);
+		if(adapter[card]->EngineUp) {
+			pr_debug("%s: SCIOCSTART: Command Failed, Engine already running.\n",
+				adapter[card]->devicename);
+			return -1;
+		}
+
+		adapter[card]->StartOnReset = 1;
+		startproc(card);
+		return 0;
+	}
+
+	case SCIOCSETSWITCH:
+	{
+		RspMessage	rcvmsg;
+		char		switchtype;
+		int 		status, err;
+
+		pr_debug("%s: SCIOSETSWITCH: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the switch type from user space
+		 */
+		if((err = verify_area(VERIFY_READ, (char *) data->dataptr, sizeof(char))))
+			return err;
+		copy_from_user(&switchtype, (char *) data->dataptr, sizeof(char));
+
+		pr_debug("%s: SCIOCSETSWITCH: Setting switch type to %d\n", adapter[card]->devicename,
+			switchtype);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType,
+						0, sizeof(char),&switchtype,&rcvmsg, SAR_TIMEOUT);
+		if(!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCSETSWITCH: Command Successful\n", adapter[card]->devicename);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETSWITCH: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+	}
+		
+	case SCIOCGETSWITCH:
+	{
+		RspMessage 	rcvmsg;
+		char		switchtype;
+		int		status, err;
+
+		pr_debug("%s: SCIOGETSWITCH: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the switch type from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, 
+			ceReqCallGetSwitchType, 0, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCGETSWITCH: Command Sucessful\n", adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSWITCH: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+
+		switchtype = rcvmsg.msg_data.byte_array[0];
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if((err = verify_area(VERIFY_WRITE, (char *) data->dataptr, sizeof(char))))
+			return err;
+		copy_to_user((char *) data->dataptr, &switchtype, sizeof(char));
+
+		return 0;
+	}
+
+	case SCIOCGETSPID:
+	{
+		RspMessage	rcvmsg;
+		char		spid[SCIOC_SPIDSIZE];
+		int		status, err;
+
+		pr_debug("%s: SCIOGETSPID: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the spid from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID,
+					data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			pr_debug("%s: SCIOCGETSPID: Command Sucessful\n", adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSPID: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+		strcpy(spid, rcvmsg.msg_data.byte_array);
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if((err = verify_area(VERIFY_WRITE, (char *) data->dataptr, sizeof(spid))))
+			return err;
+		copy_to_user((char *) data->dataptr, spid, sizeof(spid));
+
+		return 0;
+	}	
+
+	case SCIOCSETSPID:
+	{
+		RspMessage	rcvmsg;
+		char		spid[SCIOC_SPIDSIZE];
+		int 		status, err;
+
+		pr_debug("%s: DCBIOSETSPID: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the spid from user space
+		 */
+		if((err = verify_area(VERIFY_READ, (char *) data->dataptr, 
+			sizeof(spid))))
+			return err;
+		copy_from_user(spid, (char *) data->dataptr, sizeof(spid));
+
+		pr_debug("%s: SCIOCSETSPID: Setting channel %d spid to %s\n", 
+			adapter[card]->devicename, data->channel, spid);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, 
+			ceReqClass0, ceReqCallSetSPID, data->channel, 
+			strlen(spid), spid, &rcvmsg, SAR_TIMEOUT);
+		if(!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCSETSPID: Command Successful\n", 
+				adapter[card]->devicename);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETSPID: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+	}
+
+	case SCIOCGETDN:
+	{
+		RspMessage	rcvmsg;
+		char		dn[SCIOC_DNSIZE];
+		int		status, err;
+
+		pr_debug("%s: SCIOGETDN: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the dn from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber,
+					data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			pr_debug("%s: SCIOCGETDN: Command Sucessful\n", adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETDN: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+
+		strcpy(dn, rcvmsg.msg_data.byte_array);
+
+		/*
+		 * Package the dn and send to user space
+		 */
+		if((err = verify_area(VERIFY_WRITE, (char *) data->dataptr, sizeof(dn))))
+			return err;
+		copy_to_user((char *) data->dataptr, dn, sizeof(dn));
+
+		return 0;
+	}	
+
+	case SCIOCSETDN:
+	{
+		RspMessage	rcvmsg;
+		char		dn[SCIOC_DNSIZE];
+		int 		status, err;
+
+		pr_debug("%s: SCIOSETDN: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the spid from user space
+		 */
+		if((err = verify_area(VERIFY_READ, (char *) data->dataptr, 
+			sizeof(dn))))
+			return err;
+		copy_from_user(dn, (char *) data->dataptr, sizeof(dn));
+
+		pr_debug("%s: SCIOCSETDN: Setting channel %d dn to %s\n", 
+			adapter[card]->devicename, data->channel, dn);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, 
+			ceReqClass0, ceReqCallSetMyNumber, data->channel, 
+			strlen(dn),dn,&rcvmsg, SAR_TIMEOUT);
+		if(!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCSETDN: Command Successful\n", 
+				adapter[card]->devicename);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETDN: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+	}
+
+	case SCIOCTRACE:
+
+		pr_debug("%s: SCIOTRACE: ioctl received\n", adapter[card]->devicename);
+/*		adapter[card]->trace = !adapter[card]->trace; 
+		pr_debug("%s: SCIOCTRACE: Tracing turned %s\n", adapter[card]->devicename,
+			adapter[card]->trace ? "ON" : "OFF"); */
+		break;
+
+	case SCIOCSTAT:
+	{
+		boardInfo bi;
+		int err;
+
+		pr_debug("%s: SCIOSTAT: ioctl received\n", adapter[card]->devicename);
+		GetStatus(card, &bi);
+		if((err = verify_area(VERIFY_WRITE, (boardInfo *) data->dataptr,
+			sizeof(boardInfo))))
+			return err;
+		
+		copy_to_user((boardInfo *) data->dataptr, &bi, sizeof(boardInfo));
+
+		return 0;
+	}
+
+	case SCIOCGETSPEED:
+	{
+		RspMessage	rcvmsg;
+		char		speed;
+		int		status, err;
+
+		pr_debug("%s: SCIOGETSPEED: ioctl received\n", adapter[card]->devicename);
+
+		/*
+		 * Get the speed from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, 
+			ceReqCallGetCallType, data->channel, 0, 0, &rcvmsg, SAR_TIMEOUT);
+		if (!status && !rcvmsg.rsp_status) {
+			pr_debug("%s: SCIOCGETSPEED: Command Sucessful\n",
+				adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSPEED: Command Failed (status = %d)\n",
+				adapter[card]->devicename, status);
+			return status;
+		}
+
+		speed = rcvmsg.msg_data.byte_array[0];
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if((err = verify_area(VERIFY_WRITE, (char *) data->dataptr, sizeof(char))))
+			return err;
+		copy_to_user((char *) data->dataptr, &speed, sizeof(char));
+
+		return 0;
+	}
+
+	case SCIOCSETSPEED:
+		pr_debug("%s: SCIOCSETSPEED: ioctl received\n", adapter[card]->devicename);
+		break;
+
+	case SCIOCLOOPTST:
+		pr_debug("%s: SCIOCLOOPTST: ioctl received\n", adapter[card]->devicename);
+		break;
+
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+int GetStatus(int card, boardInfo *bi)
+{
+	RspMessage rcvmsg;
+	int i, status;
+
+	/*
+	 * Fill in some of the basic info about the board
+	 */
+	bi->modelid = adapter[card]->model;
+	strcpy(bi->serial_no, adapter[card]->hwconfig.serial_no);
+	strcpy(bi->part_no, adapter[card]->hwconfig.part_no);
+	bi->iobase = adapter[card]->iobase;
+	bi->rambase = adapter[card]->rambase;
+	bi->irq = adapter[card]->interrupt;
+	bi->ramsize = adapter[card]->hwconfig.ram_size;
+	bi->interface = adapter[card]->hwconfig.st_u_sense;
+	strcpy(bi->load_ver, adapter[card]->load_ver);
+	strcpy(bi->proc_ver, adapter[card]->proc_ver);
+
+	/*
+	 * Get the current PhyStats and LnkStats
+	 */
+	status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2,
+		ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+	if(!status) {
+		if(adapter[card]->model < PRI_BOARD) {
+			bi->l1_status = rcvmsg.msg_data.byte_array[2];
+			for(i = 0 ; i < BRI_CHANNELS ; i++)
+				bi->status.bristats[i].phy_stat =
+					rcvmsg.msg_data.byte_array[i];
+		}
+		else {
+			bi->l1_status = rcvmsg.msg_data.byte_array[0];
+			bi->l2_status = rcvmsg.msg_data.byte_array[1];
+			for(i = 0 ; i < PRI_CHANNELS ; i++)
+				bi->status.pristats[i].phy_stat = 
+					rcvmsg.msg_data.byte_array[i+2];
+		}
+	}
+	
+	/*
+	 * Get the call types for each channel
+	 */
+	for (i = 0 ; i < adapter[card]->nChannels ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			if (adapter[card]->model == PRI_BOARD) {
+				bi->status.pristats[i].call_type = 
+					rcvmsg.msg_data.byte_array[0];
+			}
+			else {
+				bi->status.bristats[i].call_type =
+					rcvmsg.msg_data.byte_array[0];
+			}
+		}
+	}
+	
+	/*
+	 * If PRI, get the call states and service states for each channel
+	 */
+	if (adapter[card]->model == PRI_BOARD) {
+		/*
+		 * Get the call states
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+			ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			for( i = 0 ; i < PRI_CHANNELS ; i++ )
+				bi->status.pristats[i].call_state = 
+					rcvmsg.msg_data.byte_array[i];
+		}
+
+		/*
+		 * Get the service states
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+			ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			for( i = 0 ; i < PRI_CHANNELS ; i++ )
+				bi->status.pristats[i].serv_state = 
+					rcvmsg.msg_data.byte_array[i];
+		}
+
+		/*
+		 * Get the link stats for the channels
+		 */
+		for (i = 1 ; i <= PRI_CHANNELS ; i++) {
+			status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+				ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+			if (!status) {
+				bi->status.pristats[i-1].link_stats.tx_good =
+					(unsigned long)rcvmsg.msg_data.byte_array[0];
+				bi->status.pristats[i-1].link_stats.tx_bad =
+					(unsigned long)rcvmsg.msg_data.byte_array[4];
+				bi->status.pristats[i-1].link_stats.rx_good =
+					(unsigned long)rcvmsg.msg_data.byte_array[8];
+				bi->status.pristats[i-1].link_stats.rx_bad =
+					(unsigned long)rcvmsg.msg_data.byte_array[12];
+			}
+		}
+
+		/*
+		 * Link stats for the D channel
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+			ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+			bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+			bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+			bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+		}
+
+		return 0;
+	}
+
+	/*
+	 * If BRI or POTS, Get SPID, DN and call types for each channel
+	 */
+
+	/*
+	 * Get the link stats for the channels
+	 */
+	status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+		ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+	if (!status) {
+		bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+		bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+		bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+		bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+		bi->status.bristats[0].link_stats.tx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[16];
+		bi->status.bristats[0].link_stats.tx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[20];
+		bi->status.bristats[0].link_stats.rx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[24];
+		bi->status.bristats[0].link_stats.rx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[28];
+		bi->status.bristats[1].link_stats.tx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[32];
+		bi->status.bristats[1].link_stats.tx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[36];
+		bi->status.bristats[1].link_stats.rx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[40];
+		bi->status.bristats[1].link_stats.rx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[44];
+	}
+
+	/*
+	 * Get the SPIDs
+	 */
+	for (i = 0 ; i < BRI_CHANNELS ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetSPID, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status)
+			strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array);
+	}
+		
+	/*
+	 * Get the DNs
+	 */
+	for (i = 0 ; i < BRI_CHANNELS ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetMyNumber, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status)
+			strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array);
+	}
+		
+	return 0;
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/message.c linux/drivers/isdn/sc/message.c
--- v2.1.26/linux/drivers/isdn/sc/message.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/message.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,300 @@
+/*
+ *  $Id: message.c,v 1.2 1996/11/20 17:49:54 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  message.c - functions for sending and receiving control messages
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *adapter[];
+extern unsigned int cinst;
+
+/*
+ * Obligitory function prototypes
+ */
+extern int indicate_status(int,ulong,char*);
+extern int scm_command(isdn_ctrl *);
+extern void *memcpy_fromshmem(int, void *, const void *, size_t);
+
+/*
+ * Dump message queue in shared memory to screen
+ */
+void dump_messages(int card) 
+{
+	DualPortMemory dpm;
+	unsigned long flags;
+
+	int i =0;
+	
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+	}
+
+	save_flags(flags);
+	cli();
+	outb(adapter[card]->ioport[adapter[card]->shmem_pgport], 
+		(adapter[card]->shmem_magic >> 14) | 0x80);
+	memcpy_fromshmem(card, &dpm, 0, sizeof(dpm));
+	restore_flags(flags);
+
+	pr_debug("%s: Dumping Request Queue\n", adapter[card]->devicename);
+	for (i = 0; i < dpm.req_head; i++) {
+		pr_debug("%s: Message #%d: (%d,%d,%d), link: %d\n",
+				adapter[card]->devicename, i,
+				dpm.req_queue[i].type,
+				dpm.req_queue[i].class,
+				dpm.req_queue[i].code,
+				dpm.req_queue[i].phy_link_no);
+	}
+
+	pr_debug("%s: Dumping Response Queue\n", adapter[card]->devicename);
+	for (i = 0; i < dpm.rsp_head; i++) {
+		pr_debug("%s: Message #%d: (%d,%d,%d), link: %d, status: %d\n",
+				adapter[card]->devicename, i,
+				dpm.rsp_queue[i].type,
+				dpm.rsp_queue[i].class,
+				dpm.rsp_queue[i].code,
+				dpm.rsp_queue[i].phy_link_no,
+				dpm.rsp_queue[i].rsp_status);
+	}
+
+}	
+
+/*
+ * receive a message from the board
+ */
+int receivemessage(int card, RspMessage *rspmsg) 
+{
+	DualPortMemory *dpm;
+	unsigned long flags;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+	
+	pr_debug("%s: Entered receivemessage\n",adapter[card]->devicename);
+
+	/*
+	 * See if there are messages waiting
+	 */
+	if (inb(adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) {
+		/*
+		 * Map in the DPM to the base page and copy the message
+		 */
+		save_flags(flags);
+		cli();
+		outb((adapter[card]->shmem_magic >> 14) | 0x80,
+			adapter[card]->ioport[adapter[card]->shmem_pgport]); 
+		dpm = (DualPortMemory *) adapter[card]->rambase;
+		memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]), 
+			MSG_LEN);
+		dpm->rsp_tail = (dpm->rsp_tail+1) % MAX_MESSAGES;
+		inb(adapter[card]->ioport[FIFO_READ]);
+		restore_flags(flags);
+		
+		/*
+		 * Tell the board that the message is received
+		 */
+		pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d "
+				"cnt:%d (type,class,code):(%d,%d,%d) "
+				"link:%d stat:0x%x\n",
+					adapter[card]->devicename,
+					rspmsg->sequence_no,
+					rspmsg->process_id,
+					rspmsg->time_stamp,
+					rspmsg->cmd_sequence_no,
+					rspmsg->msg_byte_cnt,
+					rspmsg->type,
+					rspmsg->class,
+					rspmsg->code,
+					rspmsg->phy_link_no, 
+					rspmsg->rsp_status);
+
+		return 0;
+	}
+	return -ENOMSG;
+}
+	
+/*
+ * send a message to the board
+ */
+int sendmessage(int card,
+		unsigned int procid,
+		unsigned int type, 
+		unsigned int class, 
+		unsigned int code,
+		unsigned int link, 
+		unsigned int data_len, 
+		unsigned int *data) 
+{
+	DualPortMemory *dpm;
+	ReqMessage sndmsg;
+	unsigned long flags;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+
+	/*
+	 * Make sure we only send CEPID messages when the engine is up
+	 * and CMPID messages when it is down
+	 */
+	if(adapter[card]->EngineUp && procid == CMPID) {
+		pr_debug("%s: Attempt to send CM message with engine up\n",
+			adapter[card]->devicename);
+		return -ESRCH;
+	}
+
+	if(!adapter[card]->EngineUp && procid == CEPID) {
+		pr_debug("%s: Attempt to send CE message with engine down\n",
+			adapter[card]->devicename);
+		return -ESRCH;
+	}
+
+	memset(&sndmsg, 0, MSG_LEN);
+	sndmsg.msg_byte_cnt = 4;
+	sndmsg.type = type;
+	sndmsg.class = class;
+	sndmsg.code = code;
+	sndmsg.phy_link_no = link;
+
+	if (data_len > 0) {
+		if (data_len > MSG_DATA_LEN)
+			data_len = MSG_DATA_LEN;
+		memcpy(&(sndmsg.msg_data), data, data_len);
+		sndmsg.msg_byte_cnt = data_len + 8;
+	}
+
+	sndmsg.process_id = procid;
+	sndmsg.sequence_no = adapter[card]->seq_no++ % 256;
+
+	/*
+	 * wait for an empty slot in the queue
+	 */
+	while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
+		SLOW_DOWN_IO;
+
+	/*
+	 * Disable interrupts and map in shared memory
+	 */
+	save_flags(flags);
+	cli();
+	outb((adapter[card]->shmem_magic >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]); 
+	dpm = (DualPortMemory *) adapter[card]->rambase;	/* Fix me */
+	memcpy_toio(&(dpm->req_queue[dpm->req_head]),&sndmsg,MSG_LEN);
+	dpm->req_head = (dpm->req_head+1) % MAX_MESSAGES;
+	outb(sndmsg.sequence_no, adapter[card]->ioport[FIFO_WRITE]);
+	restore_flags(flags);
+		
+	pr_debug("%s: Sent Message seq:%d pid:%d time:%d "
+			"cnt:%d (type,class,code):(%d,%d,%d) "
+			"link:%d\n ",
+				adapter[card]->devicename,
+				sndmsg.sequence_no,
+				sndmsg.process_id,
+				sndmsg.time_stamp,
+				sndmsg.msg_byte_cnt,
+				sndmsg.type,
+				sndmsg.class,
+				sndmsg.code,
+				sndmsg.phy_link_no); 
+		
+	return 0;
+}
+
+int send_and_receive(int card,
+		unsigned int procid, 
+		unsigned char type,
+		unsigned char class, 
+		unsigned char code,
+		unsigned char link,
+	 	unsigned char data_len, 
+		unsigned char *data, 
+		RspMessage *mesgdata,
+		int timeout) 
+{
+	int retval;
+	int tries;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+
+	adapter[card]->want_async_messages = 1;
+	retval = sendmessage(card, procid, type, class, code, link, 
+			data_len, (unsigned int *) data);
+  
+	if (retval) {
+		pr_debug("%s: SendMessage failed in SAR\n",
+			adapter[card]->devicename);
+		adapter[card]->want_async_messages = 0;
+		return -EIO;
+	}
+
+	tries = 0;
+	/* wait for the response */
+	while (tries < timeout) {
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + 1; 
+		schedule();
+		
+		pr_debug("SAR waiting..\n");
+
+		/*
+		 * See if we got our message back
+		 */
+		if ((adapter[card]->async_msg.type == type) &&
+		    (adapter[card]->async_msg.class == class) &&
+		    (adapter[card]->async_msg.code == code) &&
+		    (adapter[card]->async_msg.phy_link_no == link)) {
+
+			/*
+			 * Got it!
+			 */
+			pr_debug("%s: Got ASYNC message\n",
+				adapter[card]->devicename);
+			memcpy(mesgdata, &(adapter[card]->async_msg), 
+				sizeof(RspMessage));
+			adapter[card]->want_async_messages = 0;
+			return 0;
+		}
+
+   		tries++;
+	}
+
+	pr_debug("%s: SAR message timeout\n", adapter[card]->devicename);
+	adapter[card]->want_async_messages = 0;
+	return -ETIME;
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/message.h linux/drivers/isdn/sc/message.h
--- v2.1.26/linux/drivers/isdn/sc/message.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/message.h	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,256 @@
+/*
+ *  $Id: message.h,v 1.1 1996/11/07 13:07:47 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  message.h - structures, macros and defines useful for sending
+ *              messages to the adapter
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+/*
+ * Board message macros, defines and structures
+ */
+ 
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#define MAX_MESSAGES		32	/* Maximum messages that can be
+					   queued */
+#define MSG_DATA_LEN		48	/* Maximum size of message payload */
+#define MSG_LEN			64	/* Size of a message */
+#define CMPID			0	/* Loader message process ID */
+#define CEPID			64	/* Firmware message process ID */
+
+/*
+ * Macro to determine if a message is a loader message
+ */
+#define IS_CM_MESSAGE(mesg, tx, cx, dx)		\
+		((mesg.type == cmRspType##tx)		\
+		&&(mesg.class == cmRspClass##cx)	\
+		&&(mesg.code == cmRsp##dx))
+
+/*
+ * Macro to determine if a message is a firmware message
+ */
+#define IS_CE_MESSAGE(mesg, tx, cx, dx)		\
+		((mesg.type == ceRspType##tx)		\
+		&&(mesg.class == ceRspClass##cx)	\
+		&&(mesg.code == ceRsp##tx##dx))
+
+/* 
+ * Loader Request and Response Messages
+ */
+
+/* message types */
+#define cmReqType1			1
+#define cmReqType2			2
+#define cmRspType0			0
+#define cmRspType1			1
+#define cmRspType2			2
+#define cmRspType5			5
+
+/* message classes */
+#define cmReqClass0			0
+#define cmRspClass0			0
+
+/* message codes */
+#define cmReqHWConfig		1			/* 1,0,1 */
+#define cmReqMsgLpbk		2			/* 1,0,2 */
+#define cmReqVersion		3			/* 1,0,3 */
+#define cmReqLoadProc		1			/* 2,0,1 */
+#define cmReqStartProc		2			/* 2,0,2 */
+#define cmReqReadMem		6			/* 2,0,6 */
+#define cmRspHWConfig		cmReqHWConfig
+#define	cmRspMsgLpbk		cmReqMsgLpbk
+#define cmRspVersion		cmReqVersion
+#define cmRspLoadProc		cmReqLoadProc
+#define cmRspStartProc		cmReqStartProc
+#define	cmRspReadMem		cmReqReadMem
+#define cmRspMiscEngineUp	1			/* 5,0,1 */
+#define cmRspInvalid		0			/* 0,0,0 */
+
+
+/*
+ * Firmware Request and Response Messages
+ */
+
+/* message types */
+#define ceReqTypePhy		1
+#define ceReqTypeLnk		2
+#define ceReqTypeCall		3
+#define ceReqTypeStat		1
+#define ceRspTypeErr		0
+#define	ceRspTypePhy		ceReqTypePhy
+#define ceRspTypeLnk		ceReqTypeLnk
+#define ceRspTypeCall		ceReqTypeCall
+#define ceRspTypeStat		ceReqTypeStat
+
+/* message classes */
+#define ceReqClass0		0
+#define ceReqClass1		1
+#define ceReqClass2		2
+#define ceReqClass3		3
+#define ceRspClass0		ceReqClass0
+#define ceRspClass1		ceReqClass1
+#define ceRspClass2		ceReqClass2
+#define ceRspClass3		ceReqClass3
+
+/* message codes  (B) = BRI only, (P) = PRI only, (V) = POTS only */
+#define ceReqPhyProcInfo	1			/* 1,0,1 */
+#define ceReqPhyConnect		1			/* 1,1,1 */
+#define ceReqPhyDisconnect	2			/* 1,1,2 */
+#define ceReqPhySetParams	3			/* 1,1,3 (P) */
+#define ceReqPhyGetParams	4			/* 1,1,4 (P) */
+#define ceReqPhyStatus		1			/* 1,2,1 */
+#define ceReqPhyAcfaStatus	2			/* 1,2,2 (P) */
+#define ceReqPhyChCallState	3			/* 1,2,3 (P) */
+#define ceReqPhyChServState	4			/* 1,2,4 (P) */
+#define ceReqPhyRLoopBack	1			/* 1,3,1 */
+#define ceRspPhyProcInfo	ceReqPhyProcInfo
+#define	ceRspPhyConnect		ceReqPhyConnect
+#define ceRspPhyDisconnect	ceReqPhyDisconnect
+#define ceRspPhySetParams	ceReqPhySetParams
+#define ceRspPhyGetParams	ceReqPhyGetParams
+#define ceRspPhyStatus		ceReqPhyStatus
+#define ceRspPhyAcfaStatus	ceReqPhyAcfaStatus
+#define ceRspPhyChCallState	ceReqPhyChCallState
+#define ceRspPhyChServState	ceReqPhyChServState
+#define ceRspPhyRLoopBack	ceReqphyRLoopBack
+#define ceReqLnkSetParam	1			/* 2,0,1 */
+#define ceReqLnkGetParam	2			/* 2,0,2 */
+#define ceReqLnkGetStats	3			/* 2,0,3 */
+#define ceReqLnkWrite		1			/* 2,1,1 */
+#define ceReqLnkRead		2			/* 2,1,2 */
+#define ceReqLnkFlush		3			/* 2,1,3 */
+#define ceReqLnkWrBufTrc	4			/* 2,1,4 */
+#define ceReqLnkRdBufTrc	5			/* 2,1,5 */
+#define ceRspLnkSetParam	ceReqLnkSetParam
+#define ceRspLnkGetParam	ceReqLnkGetParam
+#define ceRspLnkGetStats	ceReqLnkGetStats
+#define ceRspLnkWrite		ceReqLnkWrite
+#define ceRspLnkRead		ceReqLnkRead
+#define ceRspLnkFlush		ceReqLnkFlush
+#define ceRspLnkWrBufTrc	ceReqLnkWrBufTrc
+#define ceRspLnkRdBufTrc	ceReqLnkRdBufTrc
+#define ceReqCallSetSwitchType	1			/* 3,0,1 */
+#define ceReqCallGetSwitchType	2			/* 3,0,2 */
+#define ceReqCallSetFrameFormat	3			/* 3,0,3 */
+#define ceReqCallGetFrameFormat	4			/* 3,0,4 */
+#define ceReqCallSetCallType	5			/* 3,0,5 */
+#define ceReqCallGetCallType	6			/* 3,0,6 */
+#define ceReqCallSetSPID	7			/* 3,0,7 (!P) */
+#define ceReqCallGetSPID	8			/* 3,0,8 (!P) */
+#define ceReqCallSetMyNumber	9			/* 3,0,9 (!P) */
+#define ceReqCallGetMyNumber	10			/* 3,0,10 (!P) */
+#define	ceRspCallSetSwitchType	ceReqCallSetSwitchType
+#define ceRspCallGetSwitchType	ceReqCallSetSwitchType
+#define ceRspCallSetFrameFormat	ceReqCallSetFrameFormat
+#define ceRspCallGetFrameFormat	ceReqCallGetFrameFormat
+#define ceRspCallSetCallType	ceReqCallSetCallType
+#define ceRspCallGetCallType	ceReqCallGetCallType
+#define ceRspCallSetSPID	ceReqCallSetSPID
+#define ceRspCallGetSPID	ceReqCallGetSPID
+#define ceRspCallSetMyNumber	ceReqCallSetMyNumber
+#define ceRspCallGetMyNumber	ceReqCallGetMyNumber
+#define ceRspStatAcfaStatus	2
+#define ceRspStat
+#define ceRspErrError		0			/* 0,0,0 */
+
+/*
+ * Call Types
+ */
+#define CALLTYPE_64K		0
+#define CALLTYPE_56K		1
+#define CALLTYPE_SPEECH		2
+#define CALLTYPE_31KHZ		3
+
+/*
+ * Link Level data contains a pointer to and the length of
+ * a buffer in shared RAM. Used by LnkRead and LnkWrite message
+ * types. Part of RspMsgStruct and ReqMsgStruct.
+ */
+typedef struct {
+	unsigned long buff_offset;
+	unsigned short msg_len;
+} LLData;
+
+
+/* 
+ * Message payload template for an HWConfig message
+ */
+typedef struct {
+	char st_u_sense;
+	char powr_sense;
+	char sply_sense;
+	unsigned char asic_id;
+	long ram_size;
+	char serial_no[13];
+	char part_no[13];
+	char rev_no[2];
+} HWConfig_pl;
+
+/*
+ * A Message
+ */
+struct message {
+	unsigned char sequence_no;
+	unsigned char process_id;
+	unsigned char time_stamp;
+	unsigned char cmd_sequence_no;	/* Rsp messages only */
+	unsigned char reserved1[3];
+	unsigned char msg_byte_cnt;
+	unsigned char type;
+	unsigned char class;
+	unsigned char code;
+	unsigned char phy_link_no;
+	unsigned char rsp_status;	/* Rsp messages only */
+	unsigned char reseved2[3];
+	union {
+		unsigned char byte_array[MSG_DATA_LEN];
+		LLData response;
+		HWConfig_pl HWCresponse;
+	} msg_data;
+};
+
+typedef struct message ReqMessage;	/* Request message */
+typedef struct message RspMessage;	/* Response message */
+
+/*
+ * The first 5010 bytes of shared memory contain the message queues,
+ * indexes and other data. This structure is its template
+ */
+typedef struct {
+	volatile ReqMessage req_queue[MAX_MESSAGES];
+	volatile RspMessage rsp_queue[MAX_MESSAGES];
+	volatile unsigned char req_head;
+	volatile unsigned char req_tail;
+	volatile unsigned char rsp_head;
+	volatile unsigned char rsp_tail;
+	volatile unsigned long signature;
+	volatile unsigned long trace_enable;
+	volatile unsigned char reserved[4];
+} DualPortMemory;
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/packet.c linux/drivers/isdn/sc/packet.c
--- v2.1.26/linux/drivers/isdn/sc/packet.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/packet.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,228 @@
+/*
+ *  $Id: packet.c,v 1.2 1996/11/20 17:49:55 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *adapter[];
+extern unsigned int cinst;
+
+extern int get_card_from_id(int);
+extern int indicate_status(int, int,ulong,char*);
+extern void *memcpy_toshmem(int, void *, const void *, size_t);
+extern void *memcpy_fromshmem(int, void *, const void *, size_t);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+                unsigned int, unsigned int, unsigned int, unsigned int *);
+
+int sndpkt(int devId, int channel, struct sk_buff *data)
+{
+	LLData	ReqLnkWrite;
+	int status;
+	int card;
+
+	card = get_card_from_id(devId);
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Send Packet: frst = 0x%x nxt = %d  f = %d n = %d\n",
+		adapter[card]->devicename,
+		adapter[card]->channel[channel].first_sendbuf,
+		adapter[card]->channel[channel].next_sendbuf,
+		adapter[card]->channel[channel].free_sendbufs,
+		adapter[card]->channel[channel].num_sendbufs);
+
+	if(!adapter[card]->channel[channel].free_sendbufs) {
+		pr_debug("%s: Out out TX buffers\n", adapter[card]->devicename);
+		return -EINVAL;
+	}
+
+	if(data->len > BUFFER_SIZE) {
+		pr_debug("%s: Data overflows buffer size (data > buffer)\n", adapter[card]->devicename);
+		return -EINVAL;
+	}
+
+	ReqLnkWrite.buff_offset = adapter[card]->channel[channel].next_sendbuf *
+		BUFFER_SIZE + adapter[card]->channel[channel].first_sendbuf;
+	ReqLnkWrite.msg_len = data->len; /* sk_buff size */
+	pr_debug("%s: Writing %d bytes to buffer offset 0x%x\n", adapter[card]->devicename,
+			ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset);
+	memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len);
+
+	/*
+	 * sendmessage
+	 */
+	pr_debug("%s: Send Packet size=%d, buf_offset=0x%x buf_indx=%d\n",
+		adapter[card]->devicename,
+		ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset,
+		adapter[card]->channel[channel].next_sendbuf);
+
+	status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
+				channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite);
+	if(status) {
+		pr_debug("%s: Failed to send packet, status = %d\n", adapter[card]->devicename, status);
+		return -1;
+	}
+	else {
+		adapter[card]->channel[channel].free_sendbufs--;
+		adapter[card]->channel[channel].next_sendbuf =
+			++adapter[card]->channel[channel].next_sendbuf ==
+			adapter[card]->channel[channel].num_sendbufs ? 0 :
+			adapter[card]->channel[channel].next_sendbuf;
+			pr_debug("%s: Packet sent successfully\n", adapter[card]->devicename);
+		dev_kfree_skb(data, FREE_WRITE);
+		indicate_status(card,ISDN_STAT_BSENT,channel,NULL);
+	}
+	return data->len;
+}
+
+void rcvpkt(int card, RspMessage *rcvmsg)
+{
+	LLData newll;
+	struct sk_buff *skb;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	switch(rcvmsg->rsp_status){
+	case 0x01:
+	case 0x02:
+	case 0x70:
+		pr_debug("%s: Error status code: 0x%x\n", adapter[card]->devicename, rcvmsg->rsp_status);
+		return;
+	case 0x00: 
+	    if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) {
+			printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n",
+				adapter[card]->devicename);
+			return;
+		}
+		skb_put(skb, rcvmsg->msg_data.response.msg_len);
+		pr_debug("%s: getting data from offset: 0x%x\n",
+			adapter[card]->devicename,rcvmsg->msg_data.response.buff_offset);
+		memcpy_fromshmem(card,
+			skb_put(skb, rcvmsg->msg_data.response.msg_len),
+		 	(char *)rcvmsg->msg_data.response.buff_offset,
+			rcvmsg->msg_data.response.msg_len);
+		adapter[card]->card->rcvcallb_skb(adapter[card]->driverId,
+			rcvmsg->phy_link_no-1, skb);
+
+	case 0x03:
+		/*
+	 	 * Recycle the buffer
+	 	 */
+		pr_debug("%s: Buffer size : %d\n", adapter[card]->devicename, BUFFER_SIZE);
+/*		memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */
+		newll.buff_offset = rcvmsg->msg_data.response.buff_offset;
+		newll.msg_len = BUFFER_SIZE;
+		pr_debug("%s: recycled buffer at offset 0x%x size %d\n", adapter[card]->devicename,
+			newll.buff_offset, newll.msg_len);
+		sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+			rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll);
+	}
+
+}
+
+int setup_buffers(int card, int c)
+{
+	unsigned int nBuffers, i, cBase;
+	unsigned int buffer_size;
+	LLData	RcvBuffOffset;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*
+	 * Calculate the buffer offsets (send/recv/send/recv)
+	 */
+	pr_debug("%s: Seting up channel buffer space in shared RAM\n", adapter[card]->devicename);
+	buffer_size = BUFFER_SIZE;
+	nBuffers = ((adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2;
+	nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers;
+	pr_debug("%s: Calculating buffer space: %d buffers, %d big\n", adapter[card]->devicename,
+		nBuffers, buffer_size);
+	if(nBuffers < 2) {
+		pr_debug("%s: Not enough buffer space\n", adapter[card]->devicename);
+		return -1;
+	}
+	cBase = (nBuffers * buffer_size) * (c - 1);
+	pr_debug("%s: Channel buffer offset from Shared RAM: 0x%x\n", adapter[card]->devicename, cBase);
+	adapter[card]->channel[c-1].first_sendbuf = BUFFER_BASE + cBase;
+	adapter[card]->channel[c-1].num_sendbufs = nBuffers / 2;
+	adapter[card]->channel[c-1].free_sendbufs = nBuffers / 2;
+	adapter[card]->channel[c-1].next_sendbuf = 0;
+	pr_debug("%s: Send buffer setup complete: first=0x%x n=%d f=%d, nxt=%d\n",
+				adapter[card]->devicename,
+				adapter[card]->channel[c-1].first_sendbuf,
+				adapter[card]->channel[c-1].num_sendbufs,
+				adapter[card]->channel[c-1].free_sendbufs,
+				adapter[card]->channel[c-1].next_sendbuf);
+
+	/*
+	 * Prep the receive buffers
+	 */
+	pr_debug("%s: Adding %d RecvBuffers:\n", adapter[card]->devicename, nBuffers /2);
+	for (i = 0 ; i < nBuffers / 2; i++) {
+		RcvBuffOffset.buff_offset = 
+			((adapter[card]->channel[c-1].first_sendbuf +
+			(nBuffers / 2) * buffer_size) + (buffer_size * i));
+		RcvBuffOffset.msg_len = buffer_size;
+		pr_debug("%s: Adding RcvBuffer #%d offset=0x%x sz=%d buffsz:%d\n",
+				adapter[card]->devicename,
+				i + 1, RcvBuffOffset.buff_offset, 
+				RcvBuffOffset.msg_len,buffer_size);
+		sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+				c, sizeof(LLData), (unsigned int *)&RcvBuffOffset);
+	} 
+	return 0;
+}
+
+int print_skb(int card,char *skb_p, int len){
+	int i,data;
+	pr_debug("%s: data at 0x%x len: 0x%x\n",adapter[card]->devicename,
+			skb_p,len);
+	for(i=1;i<=len;i++,skb_p++){
+		data = (int) (0xff & (*skb_p));
+		pr_debug("%s: data =  0x%x",adapter[card]->devicename,data);
+		if(!(i%4))
+			pr_debug(" ");
+		if(!(i%32))
+			pr_debug("\n");
+	}
+	pr_debug("\n");
+	return 0;
+}		
+
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/scioc.h linux/drivers/isdn/sc/scioc.h
--- v2.1.26/linux/drivers/isdn/sc/scioc.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/scioc.h	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,101 @@
+
+/*
+ * IOCTL Command Codes
+ */
+#define SCIOCLOAD	0x01	/* Load a firmware record */
+#define SCIOCRESET	0x02	/* Perform hard reset */
+#define SCIOCDEBUG	0x03	/* Set debug level */
+#define SCIOCREV	0x04	/* Get driver revision(s) */
+#define SCIOCSTART	0x05	/* Start the firmware */
+#define SCIOCGETSWITCH	0x06	/* Get switch type */
+#define SCIOCSETSWITCH	0x07	/* Set switch type */
+#define SCIOCGETSPID	0x08	/* Get channel SPID */
+#define SCIOCSETSPID	0x09 	/* Set channel SPID */
+#define SCIOCGETDN	0x0A	/* Get channel DN */
+#define SCIOCSETDN	0x0B 	/* Set channel DN */
+#define SCIOCTRACE	0x0C	/* Toggle trace mode */
+#define SCIOCSTAT	0x0D	/* Get line status */
+#define SCIOCGETSPEED	0x0E	/* Set channel speed */
+#define SCIOCSETSPEED	0x0F	/* Set channel speed */
+#define SCIOCLOOPTST	0x10	/* Perform loopback test */
+
+typedef struct {
+	int device;
+	int channel;
+	unsigned long command;
+	void *dataptr;
+} scs_ioctl;
+
+/* Size of strings */
+#define SCIOC_SPIDSIZE		49
+#define SCIOC_DNSIZE		SCIOC_SPIDSIZE
+#define SCIOC_REVSIZE		SCIOC_SPIDSIZE
+#define SCIOC_SRECSIZE		49
+
+typedef struct {
+	unsigned long tx_good;
+	unsigned long tx_bad;
+	unsigned long rx_good;
+	unsigned long rx_bad;
+} ChLinkStats;
+
+typedef struct {
+	char spid[49];
+	char dn[49];
+	char call_type;
+	char phy_stat;
+	ChLinkStats link_stats;
+} BRIStat;
+
+typedef BRIStat POTStat;
+
+typedef struct {
+	char call_type;
+	char call_state;
+	char serv_state;
+	char phy_stat;
+	ChLinkStats link_stats;
+} PRIStat;
+
+typedef char PRIInfo;
+typedef char BRIInfo;
+typedef char POTInfo;
+
+
+typedef struct {
+	char acfa_nos;
+	char acfa_ais;
+	char acfa_los;
+	char acfa_rra;
+	char acfa_slpp;
+	char acfa_slpn;
+	char acfa_fsrf;
+} ACFAStat;
+
+typedef struct {
+	unsigned char modelid;
+	char serial_no[13];
+	char part_no[13];
+	char load_ver[11];
+	char proc_ver[11];
+	int iobase;
+	long rambase;
+	char irq;
+	long ramsize;
+	char interface;
+	char switch_type;
+	char l1_status;
+	char l2_status;
+	ChLinkStats dch_stats;
+	ACFAStat AcfaStats;
+	union {
+		PRIStat pristats[23];
+		BRIStat bristats[2];
+		POTStat potsstats[2];
+	} status;
+	union {
+		PRIInfo priinfo;
+		BRIInfo briinfo;
+		POTInfo potsinfo;
+	} info;
+} boardInfo;
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/shmem.c linux/drivers/isdn/sc/shmem.c
--- v2.1.26/linux/drivers/isdn/sc/shmem.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/shmem.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,165 @@
+/*
+ *  $Id: shmem.c,v 1.2 1996/11/20 17:49:56 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  card.c - Card functions implementing ISDN4Linux functionality
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"		/* This must be first */
+#include "hardware.h"
+#include "card.h"
+
+/*
+ * Main adapter array
+ */
+extern board *adapter[];
+extern int cinst;
+
+/*
+ *
+ */
+void *memcpy_toshmem(int card, void *dest, const void *src, size_t n)
+{
+	unsigned long flags;
+	void *ret;
+	unsigned char ch;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return NULL;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return NULL;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) dest / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch);
+	/*
+	 * Block interrupts and load the page
+	 */
+	save_flags(flags);
+	cli();
+
+	outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]);
+	pr_debug("%s: set page to %#x\n",adapter[card]->devicename,
+		((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	ret = memcpy_toio(adapter[card]->rambase + 
+		((unsigned long) dest % 0x4000), src, n);
+	pr_debug("%s: copying %d bytes from %#x to %#x\n",adapter[card]->devicename, n,
+		 (unsigned long) src, adapter[card]->rambase + ((unsigned long) dest %0x4000));
+	restore_flags(flags);
+
+	return ret;
+}
+
+/*
+ * Reverse of above
+ */
+void *memcpy_fromshmem(int card, void *dest, const void *src, size_t n)
+{
+	unsigned long flags;
+	void *ret;
+	unsigned char ch;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return NULL;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return NULL;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) src / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch);
+	
+	
+	/*
+	 * Block interrupts and load the page
+	 */
+	save_flags(flags);
+	cli();
+
+	outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]);
+	pr_debug("%s: set page to %#x\n",adapter[card]->devicename,
+		((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	ret = memcpy_fromio(dest,(void *)(adapter[card]->rambase + 
+		((unsigned long) src % 0x4000)), n);
+/*	pr_debug("%s: copying %d bytes from %#x to %#x\n",
+		adapter[card]->devicename, n,
+		adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */
+	restore_flags(flags);
+
+	return ret;
+}
+
+void *memset_shmem(int card, void *dest, int c, size_t n)
+{
+	unsigned long flags;
+	unsigned char ch;
+	void *ret;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return NULL;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return NULL;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) dest / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n",adapter[card]->devicename,ch);
+
+	/*
+	 * Block interrupts and load the page
+	 */
+	save_flags(flags);
+	cli();
+
+	outb(((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		adapter[card]->ioport[adapter[card]->shmem_pgport]);
+	pr_debug("%s: set page to %#x\n",adapter[card]->devicename,
+		((adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	ret = memset_io(adapter[card]->rambase + 
+		((unsigned long) dest % 0x4000), c, n);
+	restore_flags(flags);
+
+	return ret;
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/sc/timer.c linux/drivers/isdn/sc/timer.c
--- v2.1.26/linux/drivers/isdn/sc/timer.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/isdn/sc/timer.c	Tue Feb 25 17:12:50 1997
@@ -0,0 +1,172 @@
+/*
+ *  $Id: timer.c,v 1.2 1996/11/20 17:49:57 fritz Exp $
+ *  Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define __NO_VERSION__
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *adapter[];
+
+extern void flushreadfifo(int);
+extern int  startproc(int);
+extern int  indicate_status(int, int, unsigned long, char *);
+extern int  sendmessage(int, unsigned int, unsigned int, unsigned int,
+        unsigned int, unsigned int, unsigned int, unsigned int *);
+
+
+/*
+ * Write the proper values into the I/O ports following a reset
+ */
+void setup_ports(int card)
+{
+
+	outb((adapter[card]->rambase >> 12), adapter[card]->ioport[EXP_BASE]);
+
+	/* And the IRQ */
+	outb((adapter[card]->interrupt | 0x80), 
+		adapter[card]->ioport[IRQ_SELECT]);
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Setup the ioports for the board that were cleared by the reset.
+ * Then, check to see if the signate has been set. Next, set the
+ * signature to a known value and issue a startproc if needed.
+ */
+void check_reset(unsigned long data)
+{
+	unsigned long flags;
+	unsigned long sig;
+	int card = (unsigned int) data;
+
+	pr_debug("%s: check_timer timer called\n", adapter[card]->devicename);
+
+	/* Setup the io ports */
+	setup_ports(card);
+
+  	save_flags(flags);
+	cli();
+	outb(adapter[card]->ioport[adapter[card]->shmem_pgport],
+		(adapter[card]->shmem_magic>>14) | 0x80);	
+	sig = (unsigned long) *((unsigned long *)(adapter[card]->rambase + SIG_OFFSET));	
+
+	/* check the signature */
+	if(sig == SIGNATURE) {
+		flushreadfifo(card);
+		restore_flags(flags);
+		/* See if we need to do a startproc */
+		if (adapter[card]->StartOnReset)
+			startproc(card);
+	}
+	else  {
+		pr_debug("%s: No signature yet, waiting another %d jiffies.\n", 
+			adapter[card]->devicename, CHECKRESET_TIME);
+		del_timer(&adapter[card]->reset_timer);
+		adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
+		add_timer(&adapter[card]->reset_timer);
+	}
+	restore_flags(flags);
+		
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Send check adapter->phystat to see if the channels are up
+ * If they are, tell ISDN4Linux that the board is up. If not,
+ * tell IADN4Linux that it is up. Always reset the timer to
+ * fire again (endless loop).
+ */
+void check_phystat(unsigned long data)
+{
+	unsigned long flags;
+	int card = (unsigned int) data;
+
+	pr_debug("%s: Checking status...\n", adapter[card]->devicename);
+	/* 
+	 * check the results of the last PhyStat and change only if
+	 * has changed drastically
+	 */
+	if (adapter[card]->nphystat && !adapter[card]->phystat) {   /* All is well */
+		pr_debug("PhyStat transition to RUN\n");
+		pr_info("%s: Switch contacted, transmitter enabled\n", 
+			adapter[card]->devicename);
+		indicate_status(card, ISDN_STAT_RUN, 0, NULL);
+	}
+	else if (!adapter[card]->nphystat && adapter[card]->phystat) {   /* All is not well */
+		pr_debug("PhyStat transition to STOP\n");
+		pr_info("%s: Switch connection lost, transmitter disabled\n", 
+			adapter[card]->devicename);
+
+		indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+	}
+
+	adapter[card]->phystat = adapter[card]->nphystat;
+
+	/* Reinitialize the timer */
+	save_flags(flags);
+	cli();
+	del_timer(&adapter[card]->stat_timer);
+	adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
+	add_timer(&adapter[card]->stat_timer);
+	restore_flags(flags);
+
+	/* Send a new cePhyStatus message */
+	sendmessage(card, CEPID,ceReqTypePhy,ceReqClass2,
+		ceReqPhyStatus,0,0,NULL);
+}
+
+/*
+ * When in trace mode, this callback is used to swap the working shared
+ * RAM page to the trace page(s) and process all received messages. It
+ * must be called often enough to get all of the messages out of RAM before
+ * it loops around.
+ * Trace messages are \n terminated strings.
+ * We output the messages in 64 byte chunks through readstat. Each chunk
+ * is scanned for a \n followed by a time stamp. If the timerstamp is older
+ * than the current time, scanning stops and the page and offset are recorded
+ * as the starting point the next time the trace timer is called. The final
+ * step is to restore the working page and reset the timer.
+ */
+void trace_timer(unsigned long data)
+{
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts and swap the first page
+	 */
+	save_flags(flags);
+	cli();
+}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/callc.c linux/drivers/isdn/teles/callc.c
--- v2.1.26/linux/drivers/isdn/teles/callc.c	Wed Oct 30 04:26:48 1996
+++ linux/drivers/isdn/teles/callc.c	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,15 @@
-/* $Id: callc.c,v 1.13 1996/06/24 17:15:55 fritz Exp $
+/* $Id: callc.c,v 1.16 1997/02/11 01:39:46 keil Exp $
  *
  * $Log: callc.c,v $
+ * Revision 1.16  1997/02/11 01:39:46  keil
+ * Changed setup-interface (incoming and outgoing)
+ *
+ * Revision 1.15  1996/11/23 11:32:20  keil
+ * windowsize = 7 X.75 bugfix Thanks to Martin Maurer
+ *
+ * Revision 1.14  1996/10/22 23:14:14  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
  * Revision 1.13  1996/06/24 17:15:55  fritz
  * corrected return code of teles_writebuf()
  *
@@ -47,7 +56,6 @@
  */
 #define __NO_VERSION__
 #include "teles.h"
-#include <asm/uaccess.h>
 
 extern struct IsdnCard cards[];
 extern int      nrcards;
@@ -58,7 +66,6 @@
 
 static int      init_ds(int chan, int incoming);
 static void     release_ds(int chan);
-static char    *strcpyupto(char *dest, char *src, char upto);
 
 static struct Fsm callcfsm =
 {NULL, 0, 0},   lcfsm =
@@ -219,19 +226,6 @@
 #define LC_D  0
 #define LC_B  1
 
-static int
-my_atoi(char *s)
-{
-        int             i, n;
-
-        n = 0;
-        if (!s)
-                return -1;
-        for (i = 0; *s >= '0' && *s <= '9'; i++, s++)
-                n = 10 * n + (*s - '0');
-        return n;
-}
-
 /*
  * Dial out
  */
@@ -240,23 +234,10 @@
 {
         isdn_ctrl      *ic = arg;
         struct Channel *chanp = fi->userdata;
-        char           *ptr;
-        char            sis[3];
 
-        /* Destination Phone-Number */
-        ptr = strcpyupto(chanp->para.called, ic->num, ',');
-        /* Source Phone-Number */
-        ptr = strcpyupto(chanp->para.calling, ptr + 1, ',');
-        if (!strcmp(chanp->para.calling, "0"))
-                chanp->para.calling[0] = '\0';
-
-        /* Service-Indicator 1 */
-        ptr = strcpyupto(sis, ptr + 1, ',');
-        chanp->para.info = my_atoi(sis);
-
-        /* Service-Indicator 2 */
-        ptr = strcpyupto(sis, ptr + 1, '\0');
-        chanp->para.info2 = my_atoi(sis);
+	chanp->para.setup = ic->parm.setup;
+	if (!strcmp(chanp->para.setup.eazmsn, "0"))
+		chanp->para.setup.eazmsn[0] = '\0';
 
         chanp->l2_active_protocol = chanp->l2_protocol;
         chanp->incoming = 0;
@@ -414,8 +395,7 @@
                  * No need to return "unknown" for calls without OAD,
                  * cause that's handled in linklevel now (replaced by '0')
                  */
-                sprintf(ic.num, "%s,%d,0,%s", chanp->para.calling, chanp->para.info,
-                        chanp->para.called);
+		ic.parm.setup = chanp->para.setup;
                 iif.statcallb(&ic);
         } else {
                 chanp->is.l4.l4l3(&chanp->is,CC_DLRL,NULL);
@@ -670,7 +650,7 @@
         ic.driver = drid;
         ic.command = ISDN_STAT_CINF;
         ic.arg = chanp->chan;
-        sprintf(ic.num, "%d", chanp->para.chargeinfo);
+        sprintf(ic.parm.num, "%d", chanp->para.chargeinfo);
         iif.statcallb(&ic);
 }
 
@@ -1234,7 +1214,7 @@
         st->l2.laptype = LAPB;
         st->l2.orig = !incoming;
         st->l2.t200 = 1000;        /* 1000 milliseconds */
-        st->l2.window = 3;
+        st->l2.window = 7;
         st->l2.n200 = 4;           /* try 4 times       */
         st->l2.t203 = 5000;        /* 5000 milliseconds */
 
@@ -1327,7 +1307,9 @@
           case (ISDN_CMD_DIAL):
                   chanp = chanlist + (ic->arg & 0xff);
                   if (chanp->debug & 1) {
-                          sprintf(tmp, "DIAL %s", ic->num);
+		  	  sprintf(tmp, "DIAL %s -> %s (%d,%d)",
+				ic->parm.setup.eazmsn, ic->parm.setup.phone,
+				ic->parm.setup.si1, ic->parm.setup.si2);
                           command_debug(chanp, tmp);
                   }
                   FsmEvent(&chanp->fi, EV_DIAL, ic);
@@ -1353,7 +1335,7 @@
           case (ISDN_CMD_SUSPEND):
                   chanp = chanlist + ic->arg;
                   if (chanp->debug & 1) {
-                          sprintf(tmp, "SUSPEND %s", ic->num);
+                          sprintf(tmp, "SUSPEND %s", ic->parm.num);
                           command_debug(chanp, tmp);
                   }
                  FsmEvent(&chanp->fi, EV_SUSPEND, ic);
@@ -1361,7 +1343,7 @@
           case (ISDN_CMD_RESUME):
                   chanp = chanlist + ic->arg;
                   if (chanp->debug & 1) {
-                          sprintf(tmp, "RESUME %s", ic->num);
+                          sprintf(tmp, "RESUME %s", ic->parm.num);
                           command_debug(chanp, tmp);
                   }
                   FsmEvent(&chanp->fi, EV_RESUME, ic);
@@ -1382,14 +1364,14 @@
                                     channel_report(i);
                             break;
                     case (1):
-                            debugflags = *(unsigned int *) ic->num;
+                            debugflags = *(unsigned int *) ic->parm.num;
                             distr_debug();
                             sprintf(tmp, "debugging flags set to %x\n", debugflags);
                             teles_putstatus(tmp);
 			    printk(KERN_DEBUG "%s", tmp);
                             break;
                     case (2):
-                            num = *(unsigned int *) ic->num;
+                            num = *(unsigned int *) ic->parm.num;
                             i = num >> 8;
                             if (i >= chancount)
                                     break;
@@ -1451,10 +1433,9 @@
 
         ptr += i;
 
-        if (user) {
-                if (copy_from_user(ptr, buf, count))
-					return -EFAULT;
-		} else
+        if (user)
+                copy_from_user(ptr, buf, count);
+        else
                 memcpy(ptr, buf, count);
         ibh->datasize = count + i;
 
@@ -1469,13 +1450,4 @@
                 return (0);
         }
 
-}
-
-static char    *
-strcpyupto(char *dest, char *src, char upto)
-{
-        while (*src && (*src != upto) && (*src != '\0'))
-                *dest++ = *src++;
-        *dest = '\0';
-        return (src);
 }
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/card.c linux/drivers/isdn/teles/card.c
--- v2.1.26/linux/drivers/isdn/teles/card.c	Mon Oct 28 11:43:22 1996
+++ linux/drivers/isdn/teles/card.c	Tue Feb 25 17:12:50 1997
@@ -1,4 +1,4 @@
-/* $Id: card.c,v 1.13 1996/07/18 11:21:24 jdenoud Exp $
+/* $Id: card.c,v 1.16 1996/10/22 23:14:16 fritz Exp $
  *
  * card.c     low level stuff for the Teles S0 isdn card
  * 
@@ -7,6 +7,15 @@
  * Beat Doebeli         log all D channel traffic
  * 
  * $Log: card.c,v $
+ * Revision 1.16  1996/10/22 23:14:16  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
+ * Revision 1.15  1996/09/29 19:41:56  fritz
+ * Bugfix: ignore unknown frames.
+ *
+ * Revision 1.14  1996/09/23 01:53:49  fritz
+ * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
+ *
  * Revision 1.13  1996/07/18 11:21:24  jdenoud
  * Use small buffers for incoming audio data
  *
@@ -59,13 +68,12 @@
 
 #define __NO_VERSION__
 #include "teles.h"
+#include "proto.h"
 
 #define INCLUDE_INLINE_FUNCS
 #include <linux/tqueue.h>
 #include <linux/interrupt.h>
 
-#include <asm/io.h>
-
 #undef DCHAN_VERBOSE
 
 extern void     tei_handler(struct PStack *st, byte pr,
@@ -77,7 +85,7 @@
 #define bytein(addr) inb_p(addr)
 
 static inline   byte
-readisac_0(unsigned int cardm, byte offset)
+readisac_0(byte * cardm, byte offset)
 {
 	return readb(cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
 }
@@ -92,7 +100,7 @@
         ((mbase)?readisac_0(mbase,ofs):readisac_3(ibase,ofs))
 
 static inline void
-writeisac_0(unsigned int cardm, byte offset, byte value)
+writeisac_0(byte * cardm, byte offset, byte value)
 {
 	writeb(value, cardm + 0x100 + ((offset & 1) ? 0x1ff : 0) + offset);
 }
@@ -119,7 +127,7 @@
 }
 
 static inline   byte
-readhscx_0(unsigned int base, byte hscx, byte offset)
+readhscx_0(byte * base, byte hscx, byte offset)
 {
 	return readb(base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
 			  ((hscx & 1) ? 0x40 : 0) + offset);
@@ -135,7 +143,7 @@
         ((mbase)?readhscx_0(mbase,hscx,ofs):readhscx_3(ibase,hscx,ofs))
 
 static inline void
-writehscx_0(unsigned int base, byte hscx, byte offset, byte data)
+writehscx_0(byte * base, byte hscx, byte offset, byte data)
 {
 	writeb(data, base + 0x180 + ((offset & 1) ? 0x1FF : 0) +
 		   ((hscx & 1) ? 0x40 : 0) + offset);
@@ -201,7 +209,7 @@
 #define HSCX_MASK 0x20
 
 static inline void
-waitforCEC_0(unsigned int base, byte hscx)
+waitforCEC_0(byte * base, byte hscx)
 {
 	long            to = 10;
 
@@ -227,7 +235,7 @@
 }
 
 static inline void
-waitforXFW_0(unsigned int base, byte hscx)
+waitforXFW_0(byte * base, byte hscx)
 {
 	long            to = 20;
 
@@ -253,7 +261,7 @@
 }
 
 static inline void
-writehscxCMDR_0(unsigned int base, byte hscx, byte data)
+writehscxCMDR_0(byte * base, byte hscx, byte data)
 {
 	long            flags;
 
@@ -1061,7 +1069,7 @@
 }
 
 static void
-initisac(unsigned int cardmem, int iobase)
+initisac(byte * cardmem, int iobase)
 {
         if (cardmem) {
                 writeisac_0(cardmem, ISAC_MASK, 0xff);
@@ -1095,17 +1103,17 @@
 	struct IsdnCard *card = cards + cardnr;
 
         if (card->membase)
-                if (card->membase < 0x10000) {
-                        card->membase <<= 4;
+                if ((unsigned long)card->membase < 0x10000) {
+                        (unsigned long)card->membase <<= 4;
                         printk(KERN_INFO
-                               "Teles membase configured DOSish, assuming 0x%x\n",
-                               card->membase);
+                               "Teles membase configured DOSish, assuming 0x%lx\n",
+                               (unsigned long)card->membase);
                 }
         if (!card->iobase) {
                 if (card->membase) {
                         printk(KERN_NOTICE
-                               "Teles 8 assumed, mem: %x irq: %d proto: %s\n",
-                               card->membase, card->interrupt,
+                               "Teles 8 assumed, mem: %lx irq: %d proto: %s\n",
+                               (long) card->membase, card->interrupt,
                                (card->protocol == ISDN_PTYPE_1TR6) ?
                                "1TR6" : "EDSS1");
                         printk(KERN_INFO "HSCX version A:%x B:%x\n",
@@ -1188,7 +1196,7 @@
                         break;
                 }
                 if (card->membase) {
-                        cfval |= (card->membase >> 9) & 0xF0;
+                        cfval |= (((unsigned int) card->membase >> 9) & 0xF0);
                 }   
                 if (bytein(card->iobase + 0) != 0x51) {
                         printk(KERN_INFO "XXX Byte at %x is %x\n",
@@ -1234,8 +1242,8 @@
                 
                 if (card->membase)
                         printk(KERN_NOTICE
-                               "Teles 16.0 found, io: %x mem: %x irq: %d proto: %s\n",
-                               card->iobase, card->membase,
+                               "Teles 16.0 found, io: %x mem: %lx irq: %d proto: %s\n",
+                               card->iobase, (long) card->membase,
                                card->interrupt,
                                (card->protocol == ISDN_PTYPE_1TR6) ?
                                "1TR6" : "EDSS1");
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/config.c linux/drivers/isdn/teles/config.c
--- v2.1.26/linux/drivers/isdn/teles/config.c	Mon Oct 28 11:46:22 1996
+++ linux/drivers/isdn/teles/config.c	Tue Feb 25 17:12:50 1997
@@ -29,20 +29,20 @@
 
 struct IsdnCard cards[] =
 {
-	{0xd0000, 15, 0xd80, ISDN_PTYPE_EURO, NULL},	/* example */
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
-	{0, 0, 0, 0, NULL},
+	{(byte *) 0xd0000, 15, 0xd80, ISDN_PTYPE_EURO, NULL},	/* example */
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
+	{NULL, 0, 0, 0, NULL},
 };
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/fsm.c linux/drivers/isdn/teles/fsm.c
--- v2.1.26/linux/drivers/isdn/teles/fsm.c	Sun May 19 05:29:30 1996
+++ linux/drivers/isdn/teles/fsm.c	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,9 @@
-/* $Id: fsm.c,v 1.2 1996/04/29 22:49:57 fritz Exp $
+/* $Id: fsm.c,v 1.3 1997/02/16 01:04:12 fritz Exp $
  *
  * $Log: fsm.c,v $
+ * Revision 1.3  1997/02/16 01:04:12  fritz
+ * Bugfix: Changed timer handling caused hang with 2.1.X
+ *
  * Revision 1.2  1996/04/29 22:49:57  fritz
  * Removed compatibility-macros.
  *
@@ -91,8 +94,6 @@
 void
 FsmDelTimer(struct FsmTimer *ft, int where)
 {
-	long            flags;
-
 #if 0
 	if (ft->fi->debug) {
 		sprintf(str, "FsmDelTimer %lx %d", ft, where);
@@ -100,11 +101,7 @@
 	}
 #endif
 
-	save_flags(flags);
-	cli();
-	if (ft->tl.next)
-		del_timer(&ft->tl);
-	restore_flags(flags);
+	del_timer(&ft->tl);
 }
 
 int
@@ -119,7 +116,7 @@
 	}
 #endif
 
-	if (ft->tl.next) {
+	if (ft->tl.next || ft->tl.prev) {
 		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
 		return -1;
 	}
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/isdnl2.c linux/drivers/isdn/teles/isdnl2.c
--- v2.1.26/linux/drivers/isdn/teles/isdnl2.c	Sun May 19 05:29:31 1996
+++ linux/drivers/isdn/teles/isdnl2.c	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,9 @@
-/* $Id: isdnl2.c,v 1.2 1996/05/17 03:46:15 fritz Exp $
+/* $Id: isdnl2.c,v 1.3 1996/11/23 11:32:57 keil Exp $
  *
  * $Log: isdnl2.c,v $
+ * Revision 1.3  1996/11/23 11:32:57  keil
+ * X.75 bugfixies; Thanks to Martin Maurer
+ *
  * Revision 1.2  1996/05/17 03:46:15  fritz
  * General cleanup.
  *
@@ -319,7 +322,7 @@
 
 	if (BufPoolGet(&ibh, st->l1.smallpool, GFP_ATOMIC, (void *) st, 10))
 		return;
-	i = sethdraddr(&(st->l2), ibh, 0);
+	i = sethdraddr(&(st->l2), ibh, !0);
 	ptr = DATAPTR(ibh);
 	ptr += i;
 	*ptr = 0x73;
@@ -565,7 +568,7 @@
 			goto noRR;
 
 		if (!BufPoolGet(&ibh2, st->l1.smallpool, GFP_ATOMIC, (void *) st, 13)) {
-			i = sethdraddr(&(st->l2), ibh2, p);
+			i = sethdraddr(&(st->l2), ibh2, !0);
 			ptr = DATAPTR(ibh2);
 			ptr += i;
 
@@ -847,7 +850,7 @@
 		*ptr++ = (l2->vr << 1) | 0x1;
 		l2->vs = (l2->vs + 1) % 128;
 	} else {
-		*ptr++ = (l2->vr << 5) | (l2->vs << 1) | 0x10;
+		*ptr++ = (l2->vr << 5) | (l2->vs << 1);
 		l2->vs = (l2->vs + 1) % 8;
 	}
 
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/isdnl3.c linux/drivers/isdn/teles/isdnl3.c
--- v2.1.26/linux/drivers/isdn/teles/isdnl3.c	Fri Jun  7 06:02:42 1996
+++ linux/drivers/isdn/teles/isdnl3.c	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,18 @@
-/* $Id: isdnl3.c,v 1.9 1996/06/06 14:22:27 fritz Exp $
+/* $Id: isdnl3.c,v 1.13 1997/02/16 12:12:51 fritz Exp $
  *
  * $Log: isdnl3.c,v $
+ * Revision 1.13  1997/02/16 12:12:51  fritz
+ * Bugfix: SI2 was nont initialized on incoming calls.
+ *
+ * Revision 1.12  1997/02/11 01:39:20  keil
+ * Changed setup-interface (incoming and outgoing)
+ *
+ * Revision 1.11  1996/09/29 19:41:58  fritz
+ * Bugfix: ignore unknown frames.
+ *
+ * Revision 1.10  1996/09/25 18:32:43  keil
+ * response for STATUS_ENQ message added
+ *
  * Revision 1.9  1996/06/06 14:22:27  fritz
  * Changed level of "non-digital call..." message, since
  * with audio support, this is quite normal.
@@ -129,7 +141,7 @@
 	/*
          * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
          */
-	switch (st->pa->info) {
+	switch (st->pa->setup.si1) {
 	  case 1:		/* Telephony                               */
 		  *p++ = 0x4;	/* BC-IE-code                              */
 		  *p++ = 0x3;	/* Length                                  */
@@ -149,21 +161,21 @@
 	/*
 	 * What about info2? Mapping to High-Layer-Compatibility?
 	 */
-	if (st->pa->calling[0] != '\0') {
+	if (st->pa->setup.eazmsn[0] != '\0') {
 		*p++ = 0x6c;
-		*p++ = strlen(st->pa->calling) + 1;
+		*p++ = strlen(st->pa->setup.eazmsn) + 1;
 		/* Classify as AnyPref. */
 		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-		teln = st->pa->calling;
+		teln = st->pa->setup.eazmsn;
 		while (*teln)
 			*p++ = *teln++ & 0x7f;
 	}
 	*p++ = 0x70;
-	*p++ = strlen(st->pa->called) + 1;
+	*p++ = strlen(st->pa->setup.phone) + 1;
 	/* Classify as AnyPref. */
 	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
 
-	teln = st->pa->called;
+	teln = st->pa->setup.phone;
 	while (*teln)
 		*p++ = *teln++ & 0x7f;
 
@@ -249,8 +261,8 @@
 	p = DATAPTR(ibh);
 	if (st->protocol == ISDN_PTYPE_1TR6) {
 		if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x01, 6))) {
-			st->pa->info = p[2];
-			st->pa->info2 = p[3];
+			st->pa->setup.si1 = p[2];
+			st->pa->setup.si2 = p[3];
 		} else
 			printk(KERN_WARNING "l3s12(1TR6): ServiceIndicator not found\n");
 	} else {
@@ -258,31 +270,32 @@
                  * Bearer Capabilities
                  */
 		if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, 0x04, 0))) {
+			st->pa->setup.si2 = 0;
 			switch (p[2] & 0x1f) {
 			  case 0x00:
                                   /* Speech */
 			  case 0x10:
                                   /* 3.1 Khz audio */
-				  st->pa->info = 1;
+				  st->pa->setup.si1 = 1;
 				  break;
 			  case 0x08:
                                   /* Unrestricted digital information */
-				  st->pa->info = 7;
+				  st->pa->setup.si1 = 7;
 				  break;
 			  case 0x09:
                                   /* Restricted digital information */
-				  st->pa->info = 2;
+				  st->pa->setup.si1 = 2;
 				  break;
 			  case 0x11:
                                   /* Unrestr. digital information  with tones/announcements */
-				  st->pa->info = 3;
+				  st->pa->setup.si1 = 3;
 				  break;
 			  case 0x18:
                                   /* Video */
-				  st->pa->info = 4;
+				  st->pa->setup.si1 = 4;
 				  break;
 			  default:
-				  st->pa->info = 0;
+				  st->pa->setup.si1 = 0;
 			}
 		} else
 			printk(KERN_WARNING "l3s12: Bearer capabilities not found\n");
@@ -291,26 +304,29 @@
 	p = DATAPTR(ibh);
 	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
 			0x70, 0)))
-		iecpy(st->pa->called, p, 1);
+		iecpy(st->pa->setup.eazmsn, p, 1);
 	else
-		strcpy(st->pa->called, "");
+		strcpy(st->pa->setup.eazmsn, "");
 
 	p = DATAPTR(ibh);
 	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
 			0x6c, 0))) {
-		if (st->protocol == ISDN_PTYPE_1TR6)
-			iecpy(st->pa->calling, p, 1);
-		else
-			iecpy(st->pa->calling, p, 2);
+		st->pa->setup.plan = p[2];
+		if (st->protocol == ISDN_PTYPE_1TR6) {
+			iecpy(st->pa->setup.phone, p, 1);
+		} else {
+			st->pa->setup.screen = p[3];
+			iecpy(st->pa->setup.phone, p, 2);
+		}
 	} else
-		strcpy(st->pa->calling, "");
+		strcpy(st->pa->setup.phone, "");
 	BufPoolRelease(ibh);
 
         if (bcfound) {
-                if (st->pa->info != 7) {
+                if (st->pa->setup.si1 != 7) {
                         printk(KERN_DEBUG "non-digital call: %s -> %s\n",
-                               st->pa->calling,
-                               st->pa->called);
+                               st->pa->setup.phone,
+                               st->pa->setup.eazmsn);
                 }
                 newl3state(st, 6);
                 st->l3.l3l4(st, CC_SETUP_IND, NULL);
@@ -391,6 +407,42 @@
 	newl3state(st, 7);
 }
 
+static void
+l3s21(struct PStack *st, byte pr, void *arg)
+{
+	struct BufHeader *dibh=arg;
+	byte           *p;
+	int             size;
+
+	BufPoolRelease(dibh);
+	
+	BufPoolGet(&dibh, st->l1.sbufpool, GFP_ATOMIC, (void *) st, 20);
+	p = DATAPTR(dibh);
+	p += st->l2.ihsize;
+	size = st->l2.ihsize;
+
+	*p++ = 0x8;
+	*p++ = 0x1;
+	*p++ = st->l3.callref;
+	*p++ = MT_STATUS;
+	size += 4;
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = 0x9E; /* answer status enquire */
+	size += 4;
+
+	*p++ = 0x14; /* CallState */
+	*p++ = 0x1;
+	*p++ = st->l3.state & 0x3f; /* ISO L3 CallState */
+	size += 3;
+
+	dibh->datasize = size;
+	i_down(st, dibh);
+
+}
+
 struct stateentry {
 	int             state;
 	byte            primitive;
@@ -432,34 +484,44 @@
 
 static struct stateentry datastatelist[] =
 {
+        {0,MT_STATUS_ENQUIRY,l3s21},
         {0,MT_SETUP,l3s12},
+        {1,MT_STATUS_ENQUIRY,l3s21},
         {1,MT_CALL_PROCEEDING,l3s6},
         {1,MT_SETUP_ACKNOWLEDGE,l3s6},
         {1,MT_RELEASE_COMPLETE,l3s4},
         {1,MT_RELEASE,l3s19},
         {1,MT_DISCONNECT,l3s7},
+        {3,MT_STATUS_ENQUIRY,l3s21},
         {3,MT_DISCONNECT,l3s7},
         {3,MT_CONNECT,l3s8},
         {3,MT_ALERTING,l3s11},
         {3,MT_RELEASE,l3s19},
         {3,MT_RELEASE_COMPLETE,l3s4},
+        {4,MT_STATUS_ENQUIRY,l3s21},
         {4,MT_CONNECT,l3s8},
         {4,MT_DISCONNECT,l3s7},
         {4,MT_RELEASE,l3s19},
         {4,MT_RELEASE_COMPLETE,l3s4},
+        {8,MT_STATUS_ENQUIRY,l3s21},
         {6,MT_SETUP,l3s12},
+        {8,MT_STATUS_ENQUIRY,l3s21},
         {7,MT_RELEASE,l3s19},
         {7,MT_RELEASE_COMPLETE,l3s4_1},
         {7,MT_DISCONNECT,l3s7},
+        {8,MT_STATUS_ENQUIRY,l3s21},
         {8,MT_RELEASE,l3s19},
         {8,MT_CONNECT_ACKNOWLEDGE,l3s17},
         {8,MT_DISCONNECT,l3s7},
         {8,MT_RELEASE_COMPLETE,l3s4_1},
+        {10,MT_STATUS_ENQUIRY,l3s21},
         {10,MT_DISCONNECT,l3s7},
         {10,MT_RELEASE,l3s19},
         {10,MT_RELEASE_COMPLETE,l3s4_1},
+        {11,MT_STATUS_ENQUIRY,l3s21},
         {11,MT_RELEASE,l3s19},
         {11,MT_RELEASE_COMPLETE,l3s4},
+        {19,MT_STATUS_ENQUIRY,l3s21},
         {19,MT_RELEASE_COMPLETE,l3s4},
 };
 
@@ -502,7 +564,7 @@
 				  datastatelist_1tr6t[i].rout(st, pr, ibh);
 			  break;
 #endif
-		  default:	/* E-DSS1 */
+		  case PROTO_EURO:	/* E-DSS1 */
 			  for (i = 0; i < datasllen; i++)
 				  if ((st->l3.state == datastatelist[i].state) &&
 				      (mt == datastatelist[i].primitive))
@@ -514,6 +576,10 @@
 				 		st->l3.state, mt);
 			  } else
 				  datastatelist[i].rout(st, pr, ibh);
+			  break;
+		  default:
+		          BufPoolRelease(ibh);
+			  break;
 		}
 	} else if (pr == DL_UNIT_DATA) {
 		ptr = DATAPTR(ibh);
@@ -540,7 +606,7 @@
 				  datastatelist_1tr6t[i].rout(st, pr, ibh);
 			  break;
 #endif
-		  default:	/* E-DSS1 */
+		  case PROTO_EURO:	/* E-DSS1 */
 			  for (i = 0; i < datasllen; i++)
 				  if ((st->l3.state == datastatelist[i].state) &&
 				      (mt == datastatelist[i].primitive))
@@ -552,6 +618,10 @@
 				 		st->l3.state, mt);
 			  } else
 				  datastatelist[i].rout(st, pr, ibh);
+			  break;
+		  default:
+		          BufPoolRelease(ibh);
+			  break;
 		}
 	}
 }
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/l3_1TR6.c linux/drivers/isdn/teles/l3_1TR6.c
--- v2.1.26/linux/drivers/isdn/teles/l3_1TR6.c	Fri Jun  7 06:02:42 1996
+++ linux/drivers/isdn/teles/l3_1TR6.c	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,15 @@
-/* $Id: l3_1TR6.c,v 1.4 1996/06/06 14:22:28 fritz Exp $
+/* $Id: l3_1TR6.c,v 1.7 1997/02/11 01:38:55 keil Exp $
  *
  * $Log: l3_1TR6.c,v $
+ * Revision 1.7  1997/02/11 01:38:55  keil
+ * Changed setup-interface (incoming and outgoing)
+ *
+ * Revision 1.6  1996/09/25 18:34:57  keil
+ * missing states in 1TR6 Statemachine added
+ *
+ * Revision 1.5  1996/09/23 01:53:51  fritz
+ * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
+ *
  * Revision 1.4  1996/06/06 14:22:28  fritz
  * Changed level of "non-digital call..." message, since
  * with audio support, this is quite normal.
@@ -19,6 +28,8 @@
  *
  */
 
+#include	"proto.h"
+
 static void
 l3_1TR6_message(struct PStack *st, int mt, int pd)
 {
@@ -55,37 +66,37 @@
 	*p++ = st->l3.callref;
 	*p++ = MT_N1_SETUP;
 
-	if ('S' == (st->pa->called[0] & 0x5f)) {	/* SPV ??? */
+	if ('S' == (st->pa->setup.phone[0] & 0x5f)) {	/* SPV ??? */
 		/* NSF SPV */
 		*p++ = WE0_netSpecFac;
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_SPV;	/* SPV */
-		*p++ = st->pa->info; /* 0 for all Services */
-		*p++ = st->pa->info2; /* 0 for all Services */
+		*p++ = st->pa->setup.si1; /* 0 for all Services */
+		*p++ = st->pa->setup.si2; /* 0 for all Services */
 		*p++ = WE0_netSpecFac;
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_Activate;	/* aktiviere SPV (default) */
-		*p++ = st->pa->info; /* 0 for all Services */
-		*p++ = st->pa->info2; /* 0 for all Services */
+		*p++ = st->pa->setup.si1; /* 0 for all Services */
+		*p++ = st->pa->setup.si2; /* 0 for all Services */
 	}
-	if (st->pa->calling[0] != '\0') {
+	if (st->pa->setup.eazmsn[0]) {
 		*p++ = WE0_origAddr;
-		*p++ = strlen(st->pa->calling) + 1;
+		*p++ = strlen(st->pa->setup.eazmsn) + 1;
 		/* Classify as AnyPref. */
 		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
-		teln = st->pa->calling;
+		teln = st->pa->setup.eazmsn;
 		while (*teln)
 			*p++ = *teln++ & 0x7f;
 	}
 	*p++ = WE0_destAddr;
-	teln = st->pa->called;
-	if ('S' != (st->pa->called[0] & 0x5f)) {	/* Keine SPV ??? */
-		*p++ = strlen(st->pa->called) + 1;
+	teln = st->pa->setup.phone;
+	if ('S' != (st->pa->setup.phone[0] & 0x5f)) {	/* Keine SPV ??? */
+		*p++ = strlen(st->pa->setup.phone) + 1;
 		st->pa->spv = 0;
 	} else {		/* SPV */
-		*p++ = strlen(st->pa->called);
+		*p++ = strlen(st->pa->setup.phone);
 		teln++;		/* skip S */
 		st->pa->spv = 1;
 	}
@@ -98,8 +109,8 @@
 	/* Codesatz 6 fuer Service */
 	*p++ = WE6_serviceInd;
 	*p++ = 2;		/* len=2 info,info2 */
-	*p++ = st->pa->info;
-	*p++ = st->pa->info2;
+	*p++ = st->pa->setup.si1;
+	*p++ = st->pa->setup.si2;
 
 	dibh->datasize = p - DATAPTR(dibh);
 
@@ -130,24 +141,24 @@
 	p = DATAPTR(ibh);
 
 	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize, WE6_serviceInd, 6))) {
-		st->pa->info = p[2];
-		st->pa->info2 = p[3];
+		st->pa->setup.si1 = p[2];
+		st->pa->setup.si2 = p[3];
 	} else
 		printk(KERN_INFO "l3s12(1TR6): ServiceIndicator not found\n");
 
 	p = DATAPTR(ibh);
 	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
 			WE0_destAddr, 0)))
-		iecpy(st->pa->called, p, 1);
+		iecpy(st->pa->setup.eazmsn, p, 1);
 	else
-		strcpy(st->pa->called, "");
+		strcpy(st->pa->setup.eazmsn, "");
 
 	p = DATAPTR(ibh);
 	if ((p = findie(p + st->l2.uihsize, ibh->datasize - st->l2.uihsize,
 			WE0_origAddr, 0))) {
-		iecpy(st->pa->calling, p, 1);
+		iecpy(st->pa->setup.phone, p, 1);
 	} else
-		strcpy(st->pa->calling, "");
+		strcpy(st->pa->setup.phone, "");
 
 	p = DATAPTR(ibh);
 	st->pa->spv = 0;
@@ -159,10 +170,10 @@
 	BufPoolRelease(ibh);
 
         /* Signal all services, linklevel takes care of Service-Indicator */
-	if (st->pa->info != 7) {
+	if (st->pa->setup.si1 != 7) {
                 printk(KERN_DEBUG "non-digital call: %s -> %s\n",
-                       st->pa->calling,
-                       st->pa->called);
+                       st->pa->setup.phone,
+                       st->pa->setup.eazmsn);
 	}
         newl3state(st, 6);
         st->l3.l3l4(st, CC_SETUP_IND, NULL);
@@ -387,14 +398,14 @@
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_SPV;	/* SPV */
-		*p++ = st->pa->info;
-		*p++ = st->pa->info2;
+		*p++ = st->pa->setup.si1;
+		*p++ = st->pa->setup.si2;
 		*p++ = WE0_netSpecFac;
 		*p++ = 4;	/* Laenge */
 		*p++ = 0;
 		*p++ = FAC_Activate;	/* aktiviere SPV */
-		*p++ = st->pa->info;
-		*p++ = st->pa->info2;
+		*p++ = st->pa->setup.si1;
+		*p++ = st->pa->setup.si2;
 	}
 	dibh->datasize = p - DATAPTR(dibh);
 
@@ -404,7 +415,7 @@
 }
 
 static void
-l3_1tr6_ignore(struct PStack *st, byte pr, void *arg)
+l3_1tr6_reset(struct PStack *st, byte pr, void *arg)
 {
 	newl3state(st, 0);
 }
@@ -453,16 +464,36 @@
 static struct stateentry downstatelist_1tr6t[] =
 {
 	{0, CC_SETUP_REQ, l3_1tr6_setup},
+	{1, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+	{1, CC_RELEASE_REQ, l3_1tr6_rel_req},
+        {1, CC_DLRL, l3_1tr6_reset},
+	{2, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+	{2, CC_RELEASE_REQ, l3_1tr6_rel_req},
+        {2, CC_DLRL, l3_1tr6_reset},
+	{3, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+	{3, CC_RELEASE_REQ, l3_1tr6_rel_req},
+        {3, CC_DLRL, l3_1tr6_reset},
 	{4, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{6, CC_REJECT_REQ, l3_1tr6_ignore},
+	{4, CC_RELEASE_REQ, l3_1tr6_rel_req},
+        {4, CC_DLRL, l3_1tr6_reset},
+	{6, CC_REJECT_REQ, l3_1tr6_reset},
+	{6, CC_RELEASE_REQ, l3_1tr6_rel_req},
 	{6, CC_SETUP_RSP, l3_1tr6_conn},
 	{6, CC_ALERTING_REQ, l3_1tr6_alert},
+        {6, CC_DLRL, l3_1tr6_reset},
 	{7, CC_SETUP_RSP, l3_1tr6_conn},
+	{7, CC_RELEASE_REQ, l3_1tr6_rel_req},
 	{7, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-        {7, CC_DLRL, l3_1tr6_disconn_req},
+        {7, CC_DLRL, l3_1tr6_reset},
 	{8, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
+	{8, CC_RELEASE_REQ, l3_1tr6_rel_req},
+        {8, CC_DLRL, l3_1tr6_reset},
 	{10, CC_DISCONNECT_REQ, l3_1tr6_disconn_req},
-	{12, CC_RELEASE_REQ, l3_1tr6_rel_req}
+	{10, CC_RELEASE_REQ, l3_1tr6_rel_req},
+        {10, CC_DLRL, l3_1tr6_reset},
+	{12, CC_RELEASE_REQ, l3_1tr6_rel_req},
+        {12, CC_DLRL, l3_1tr6_reset},
+        {19, CC_DLRL, l3_1tr6_reset},
 };
 
 static int      downsl_1tr6t_len = sizeof(downstatelist_1tr6t) /
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/l3_1TR6.h linux/drivers/isdn/teles/l3_1TR6.h
--- v2.1.26/linux/drivers/isdn/teles/l3_1TR6.h	Sun May 19 05:29:31 1996
+++ linux/drivers/isdn/teles/l3_1TR6.h	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,9 @@
-/* $Id: l3_1TR6.h,v 1.3 1996/04/30 21:53:48 isdn4dev Exp $
+/* $Id: l3_1TR6.h,v 1.4 1996/09/23 01:53:52 fritz Exp $
  *
  * $Log: l3_1TR6.h,v $
+ * Revision 1.4  1996/09/23 01:53:52  fritz
+ * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
+ *
  * Revision 1.3  1996/04/30 21:53:48  isdn4dev
  * Bugs, SPV, Logging in q931.c  Karsten Keil
  *
@@ -11,9 +14,6 @@
  */
 #ifndef l3_1TR6
 #define l3_1TR6
-
-#define PROTO_DIS_N0 0x40
-#define PROTO_DIS_N1 0x41
 
 /*
  * MsgType N0
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/llglue.c linux/drivers/isdn/teles/llglue.c
--- v2.1.26/linux/drivers/isdn/teles/llglue.c	Mon Oct 28 10:57:46 1996
+++ linux/drivers/isdn/teles/llglue.c	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,9 @@
-/* $Id: llglue.c,v 1.6 1996/06/03 20:03:39 fritz Exp $
+/* $Id: llglue.c,v 1.7 1996/10/22 23:14:17 fritz Exp $
  *
  * $Log: llglue.c,v $
+ * Revision 1.7  1996/10/22 23:14:17  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
  * Revision 1.6  1996/06/03 20:03:39  fritz
  * Fixed typos.
  *
@@ -46,10 +49,9 @@
 	byte           *p;
 
 	for (p = buf, count = 0; count < len; p++, count++) {
-		if (user) {
-			put_user(*teles_status_read, p);
-			teles_status_read++; 
-		} else
+		if (user)
+			put_user(*teles_status_read++, p);
+		else
 			*p++ = *teles_status_read++;
 		if (teles_status_read > teles_status_end)
 			teles_status_read = teles_status_buf;
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/mod.c linux/drivers/isdn/teles/mod.c
--- v2.1.26/linux/drivers/isdn/teles/mod.c	Fri Dec 27 02:03:22 1996
+++ linux/drivers/isdn/teles/mod.c	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,12 @@
-/* $Id: mod.c,v 1.1 1996/04/13 10:27:02 fritz Exp $
+/* $Id: mod.c,v 1.3 1997/02/14 12:23:31 fritz Exp $
  *
  * $Log: mod.c,v $
+ * Revision 1.3  1997/02/14 12:23:31  fritz
+ * Added support for new insmod parameter handling.
+ *
+ * Revision 1.2  1997/02/10 11:45:14  fritz
+ * More changes for Kernel 2.1.X compatibility.
+ *
  * Revision 1.1  1996/04/13 10:27:02  fritz
  * Initial revision
  *
@@ -14,7 +20,7 @@
 int             nrcards;
 
 typedef struct {
-	unsigned int	membase;
+	byte           *membase;
 	int             interrupt;
 	unsigned int    iobase;
 	unsigned int    protocol;
@@ -40,6 +46,13 @@
 	{0, 0, 0, 0},
 };
 
+#ifdef MODULE
+#if (LINUX_VERSION_CODE > 0x020111)
+MODULE_PARM(io, "1-64i");
+MODULE_PARM(teles_id, "s");
+#endif
+#endif
+
 void
 teles_mod_dec_use_count(void)
 {
@@ -53,7 +66,6 @@
 }
 
 #ifdef MODULE
-EXPORT_NO_SYMBOLS;
 #define teles_init init_module
 #else
 void teles_setup(char *str, int *ints)
@@ -74,7 +86,7 @@
                         j++; argc--;
                 }
                 if (argc) {
-                        io[i].membase   = ints[j];
+                        io[i].membase   = (byte *)ints[j];
                         j++; argc--;
                 }
                 if (argc) {
@@ -116,7 +128,14 @@
                 CallcNew();
                 ll_init();
 
+		/* 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 "Teles module installed\n");
 #endif
                 return (0);
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/q931.c linux/drivers/isdn/teles/q931.c
--- v2.1.26/linux/drivers/isdn/teles/q931.c	Fri Jun  7 06:02:42 1996
+++ linux/drivers/isdn/teles/q931.c	Tue Feb 25 17:12:50 1997
@@ -1,4 +1,4 @@
-/* $Id: q931.c,v 1.5 1996/06/03 20:03:40 fritz Exp $
+/* $Id: q931.c,v 1.6 1996/09/23 01:53:53 fritz Exp $
  *
  * q931.c               code to decode ITU Q.931 call control messages
  * 
@@ -14,6 +14,9 @@
  *
  * 
  * $Log: q931.c,v $
+ * Revision 1.6  1996/09/23 01:53:53  fritz
+ * Bugfix: discard unknown frames (non-EDSS1 and non-1TR6).
+ *
  * Revision 1.5  1996/06/03 20:03:40  fritz
  * Fixed typos.
  *
@@ -35,6 +38,7 @@
 
 #define __NO_VERSION__
 #include "teles.h"
+#include "proto.h"
 #include "l3_1TR6.h"
 
 byte           *
@@ -1083,7 +1087,7 @@
 			}
 			buf += buf[1] + 2;
 		}
-	} else {	/* EURO */
+	} else if (buf[0]==PROTO_EURO) {	/* EURO */
 		/* locate message type */
 		for (i = 0; i < MTSIZE; i++)
 			if (mtlist[i].nr == buf[3])
@@ -1144,6 +1148,8 @@
 			buf += buf[1] + 2;
 		}
 	}
+	else dp += sprintf(dp,"Unnown frame type %.2x, ignored\n",buf[0]);
+
 	dp += sprintf(dp, "\n");
 	teles_putstatus(sp->dlogspace);
 }
diff -u --recursive --new-file v2.1.26/linux/drivers/isdn/teles/teles.h linux/drivers/isdn/teles/teles.h
--- v2.1.26/linux/drivers/isdn/teles/teles.h	Mon Oct 28 11:48:39 1996
+++ linux/drivers/isdn/teles/teles.h	Tue Feb 25 17:12:50 1997
@@ -1,6 +1,9 @@
-/* $Id: teles.h,v 1.2 1996/04/30 21:52:04 isdn4dev Exp $
+/* $Id: teles.h,v 1.3 1997/02/11 01:40:36 keil Exp $
  *
  * $Log: teles.h,v $
+ * Revision 1.3  1997/02/11 01:40:36  keil
+ * New Param structure
+ *
  * Revision 1.2  1996/04/30 21:52:04  isdn4dev
  * SPV for 1TR6 - Karsten
  *
@@ -14,6 +17,8 @@
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
@@ -26,9 +31,6 @@
 #include <linux/isdnif.h>
 #include <linux/tty.h>
 
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
 #define PH_ACTIVATE   1
 #define PH_DATA       2
 #define PH_DEACTIVATE 3
@@ -284,11 +286,7 @@
 	int             cause;
 	int             bchannel;
 	int             callref;     /* TEI-Number                      */
-	int             itc;
-	int             info;	     /* Service-Indicator               */
-	int             info2;	     /* Service-Indicator, second octet */
-	char            calling[40]; /* Called Id                       */
-	char            called[40];  /* Caller Id                       */
+	setup_parm      setup;       /* from isdnif.h numbers and Serviceindicator */
 	int             chargeinfo;  /* Charge Info - only for 1tr6 in
 				      * the moment 
 				      */
@@ -307,7 +305,7 @@
 };
 
 struct HscxState {
-	unsigned int	membase;
+	byte           *membase;
 	int             iobase;
 	int             inuse, init, active;
 	struct BufPool  sbufpool, rbufpool, smallpool;
@@ -330,7 +328,7 @@
 #ifdef DEBUG_MAGIC
 	int             magic;
 #endif
-	unsigned int	membase;
+	byte           *membase;
 	int             iobase;
 	struct BufPool  sbufpool, rbufpool, smallpool;
 	struct PStack  *stlist;
@@ -352,7 +350,7 @@
 };
 
 struct IsdnCard {
-	unsigned int	membase;
+	byte           *membase;
 	int             interrupt;
 	unsigned int    iobase;
 	int             protocol;	/* EDSS1 or 1TR6 */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.1.26/linux/drivers/net/3c59x.c	Sun Feb  2 05:18:35 1997
+++ linux/drivers/net/3c59x.c	Fri Feb 21 14:58:33 1997
@@ -36,8 +36,12 @@
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
+
+#ifdef CONFIG_PCI
 #include <linux/pci.h>
 #include <linux/bios32.h>
+#endif
+
 #include <linux/timer.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -79,7 +83,10 @@
 int vortex_debug = 1;
 #endif
 
+#ifdef CONFIG_PCI
 static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
+#endif
+
 static const char *product_names[] = {
 	"3c590 Vortex 10Mbps",
 	"3c595 Vortex 100baseTX",
@@ -336,6 +343,7 @@
 {
 	int cards_found = 0;
 
+#ifdef CONFIG_PCI
 	if (pcibios_present()) {
 		static int pci_index = 0;
 		static int board_index = 0;
@@ -388,6 +396,7 @@
 			}
 		}
 	}
+#endif /* CONFIG_PCI */
 
 	/* Now check all slots of the EISA bus. */
 	if (EISA_bus) {
diff -u --recursive --new-file v2.1.26/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.26/linux/drivers/net/Config.in	Sun Feb  2 05:18:35 1997
+++ linux/drivers/net/Config.in	Fri Feb 21 14:58:33 1997
@@ -137,12 +137,11 @@
       bool 'Soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
       bool 'Soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
       bool 'Soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200
+      bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
+      bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
       if [ "$CONFIG_M586" = "y" -o "$CONFIG_M686" = "y" ]; then
-        if [ "$CONFIG_SOUNDMODEM_AFSK1200" != "n" ]; then
-          bool 'Soundmodem 1200 baud AFSK using floating point' CONFIG_SOUNDMODEM_AFSK1200_FP
-        fi
+        bool 'Soundmodem using floating point' CONFIG_SOUNDMODEM_FLOAT
       fi
-      bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
     fi
   fi
   tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
diff -u --recursive --new-file v2.1.26/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.26/linux/drivers/net/Makefile	Sun Feb  2 05:18:36 1997
+++ linux/drivers/net/Makefile	Fri Feb 21 14:58:33 1997
@@ -7,6 +7,10 @@
 # are difficult for users to deal with.
 include CONFIG
 
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
 L_TARGET := net.a
 L_OBJS   := auto_irq.o
 M_OBJS   :=
@@ -649,12 +653,14 @@
 endif
 
 ifeq ($(CONFIG_SOUNDMODEM),y)
-L_OBJS += soundmodem.o
+ALL_SUB_DIRS += soundmodem
+SUB_DIRS += soundmodem
 CONFIG_HDLCDRV_BUILTIN = y
 else
   ifeq ($(CONFIG_SOUNDMODEM),m)
   CONFIG_HDLCDRV_MODULE = y
-  M_OBJS += soundmodem.o
+  ALL_SUB_DIRS += soundmodem
+  MOD_SUB_DIRS += soundmodem
   endif
 endif
 
@@ -667,7 +673,6 @@
   MX_OBJS += hdlcdrv.o
   endif
 endif
-
 
 ifeq ($(CONFIG_VENDOR_SANGOMA),y)
   M_OBJS += sdladrv.o
diff -u --recursive --new-file v2.1.26/linux/drivers/net/baycom.c linux/drivers/net/baycom.c
--- v2.1.26/linux/drivers/net/baycom.c	Tue Dec 31 00:30:00 1996
+++ linux/drivers/net/baycom.c	Fri Feb 21 14:58:34 1997
@@ -73,7 +73,6 @@
 /*****************************************************************************/
 
 #include <linux/module.h>
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
@@ -85,7 +84,6 @@
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include <asm/uaccess.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/netdevice.h>
@@ -94,6 +92,48 @@
 
 /* --------------------------------------------------------------------- */
 
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
 #define BAYCOM_DEBUG
 
 /*
@@ -101,7 +141,6 @@
  */
 #define BAYCOM_OPTIONS_SOFTDCD  1
 
-
 /* --------------------------------------------------------------------- */
 
 static const char bc_drvname[] = "baycom";
@@ -897,9 +936,6 @@
 
 /* --------------------------------------------------------------------- */
 
-#ifdef MODULE
-static
-#endif /* MODULE */
 int baycom_init(void)
 {
 	int i, j, found = 0;
@@ -946,12 +982,23 @@
 /*
  * command line settable parameters
  */
-char *mode = NULL;
-int iobase = 0x3f8;
-int irq = 4;
+static char *mode = NULL;
+static int iobase = 0x3f8;
+static int irq = 4;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
 MODULE_PARM(mode, "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; eg. ser12* or par96");
 MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
 MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "baycom irq number");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12, par96 and picpar amateur radio modem driver");
+
+#endif
 
 int init_module(void)
 {
@@ -996,7 +1043,7 @@
 	int i;
 
 	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
-	if ((i >= NR_PORTS) || (ints[0] < 3)) {
+	if ((i >= NR_PORTS) || (ints[0] < 2)) {
 		printk(KERN_INFO "%s: too many or invalid interface "
 		       "specifications\n", bc_drvname);
 		return;
diff -u --recursive --new-file v2.1.26/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
--- v2.1.26/linux/drivers/net/cs89x0.c	Sun Feb  2 05:18:36 1997
+++ linux/drivers/net/cs89x0.c	Fri Feb 21 14:58:34 1997
@@ -1180,3 +1180,4 @@
  *  tab-width: 8
  * End:
  *
+ */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/dlci.c linux/drivers/net/dlci.c
--- v2.1.26/linux/drivers/net/dlci.c	Sun Feb  2 05:18:39 1997
+++ linux/drivers/net/dlci.c	Fri Feb 21 14:58:34 1997
@@ -296,7 +296,7 @@
 	if (!get)
 	{
 		if(copy_from_user(&config, conf, sizeof(struct dlci_conf)))
-			return -FAULT;
+			return -EFAULT;
 		if (config.flags & ~DLCI_VALID_FLAGS)
 			return(-EINVAL);
 		memcpy(&dlp->config, &config, sizeof(struct dlci_conf));
diff -u --recursive --new-file v2.1.26/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c
--- v2.1.26/linux/drivers/net/hdlcdrv.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/hdlcdrv.c	Fri Feb 21 14:58:34 1997
@@ -44,13 +44,16 @@
 #include <linux/malloc.h>
 #include <linux/errno.h>
 #include <asm/bitops.h>
-#include <asm/uaccess.h>
+
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/hdlcdrv.h>
-#include <net/ax25.h>
+#ifdef CONFIG_AX25
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h> 
+#endif /* CONFIG_AX25 */
 
 /* make genksyms happy */
 #include <linux/ip.h>
@@ -61,6 +64,61 @@
 /* --------------------------------------------------------------------- */
 
 /*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x20115
+extern __inline__ void dev_init_buffers(struct device *dev)
+{
+        int i;
+        for(i=0;i<DEV_NUMBUFFS;i++)
+        {
+                skb_queue_head_init(&dev->buffs[i]);
+        }
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
  * The name of the card. Is used for messages and in the requests for
  * io regions, irqs and dma channels
  */
@@ -518,7 +576,11 @@
 
 /* --------------------------------------------------------------------- */
 
+#if LINUX_VERSION_CODE >= 0x20119
 static struct net_device_stats *hdlcdrv_get_stats(struct device *dev)
+#else
+static struct enet_statistics *hdlcdrv_get_stats(struct device *dev)
+#endif
 {
 	struct hdlcdrv_state *sm;
 
@@ -743,7 +805,9 @@
  */
 static int hdlcdrv_probe(struct device *dev)
 {
-	struct hdlcdrv_channel_params dflt_ch_params = { 20, 2, 10, 40, 0 };
+	const struct hdlcdrv_channel_params dflt_ch_params = { 
+		20, 2, 10, 40, 0 
+	};
 	struct hdlcdrv_state *s;
 
 	if (!dev)
@@ -887,21 +951,49 @@
 
 /* --------------------------------------------------------------------- */
 
+#if LINUX_VERSION_CODE >= 0x20115
+
 EXPORT_SYMBOL(hdlcdrv_receiver);
 EXPORT_SYMBOL(hdlcdrv_transmitter);
 EXPORT_SYMBOL(hdlcdrv_arbitrate);
 EXPORT_SYMBOL(hdlcdrv_register_hdlcdrv);
 EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv);
 
+#else
+
+static struct symbol_table hdlcdrv_syms = {
+#include <linux/symtab_begin.h>
+        X(hdlcdrv_receiver),
+        X(hdlcdrv_transmitter),
+        X(hdlcdrv_arbitrate),
+        X(hdlcdrv_register_hdlcdrv),
+        X(hdlcdrv_unregister_hdlcdrv),
+#include <linux/symtab_end.h>
+};
+
+#endif
+
 /* --------------------------------------------------------------------- */
 
 #ifdef MODULE
 
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
 int init_module(void)
 {
 	printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
 	printk(KERN_INFO "hdlcdrv: version 0.2 compiled %s %s\n", 
 	       __TIME__, __DATE__);
+#if LINUX_VERSION_CODE < 0x20115
+        register_symtab(&hdlcdrv_syms);
+#endif
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.26/linux/drivers/net/ltpc.c linux/drivers/net/ltpc.c
--- v2.1.26/linux/drivers/net/ltpc.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/ltpc.c	Fri Feb 21 14:58:34 1997
@@ -830,6 +830,15 @@
 	/* Actually netatalk needs fixing! */
 }
 
+static int ltpc_hard_header (struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+	if(debug&DEBUG_VERBOSE)
+		printk("ltpc_hard_header called for device %s\n",
+			dev->name);
+	return 0;
+}
+
 static int ltpc_init(struct device *dev)
 {
 	/* Initialize the device structure. */
@@ -837,6 +846,7 @@
 	/* Fill in the fields of the device structure with ethernet-generic values. */
 	ltalk_setup(dev);
 	dev->hard_start_xmit = ltpc_xmit;
+	dev->hard_header = ltpc_hard_header;
 
 	dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
 	if(!dev->priv)
@@ -855,6 +865,8 @@
 	dev->do_ioctl = &ltpc_ioctl;
 
 	dev->set_multicast_list = &set_multicast_list;
+	dev->mc_list = NULL;
+
 	return 0;
 }
 
@@ -874,23 +886,23 @@
 		ltpc_poll_counter--;
 	}
   
-	if (!dev) return;  /* we've been downed */
+	if (!dev)
+		return;  /* we've been downed */
 
-	if (dev->irq) {
+	if (dev->irq) 
+	{
 		/* we're set up for interrupts */
 		if (0xf8 != inb_p(dev->base_addr+7)) {
 			/* trigger an interrupt */
 			(void) inb_p(dev->base_addr+6);
 		}
-		ltpc_timer.expires = 100;
+		ltpc_timer.expires = jiffies+100;
 	} else {
 		/* we're strictly polling mode */
 		idle(dev);
-		ltpc_timer.expires = 5;
+		ltpc_timer.expires = jiffies+5;
 	}
 
-	ltpc_timer.expires += jiffies;  /* 1.2 to 1.3 change... */
-
 	add_timer(&ltpc_timer);
 }
 
@@ -1188,6 +1200,8 @@
 		ltpc_timer.expires = 5;
 		  /* polled mode -- 20 times per second */
 	}
+
+	ltpc_timer.expires += jiffies;  /* 1.2 to 1.3 change... */
 
 	add_timer(&ltpc_timer);
 
diff -u --recursive --new-file v2.1.26/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c
--- v2.1.26/linux/drivers/net/myri_sbus.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/myri_sbus.c	Fri Feb 21 14:58:34 1997
@@ -742,6 +742,7 @@
 		if (ndisc_eth_hook)
 			return (ndisc_eth_hook(eth->h_dest, dev, skb));
 #endif
+		break;
 #endif	
 	default:
 		printk(KERN_DEBUG 
diff -u --recursive --new-file v2.1.26/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.26/linux/drivers/net/ne.c	Tue Dec 31 00:30:01 1996
+++ linux/drivers/net/ne.c	Fri Feb 21 14:58:34 1997
@@ -75,6 +75,7 @@
 	{PCI_VENDOR_ID_REALTEK,		PCI_DEVICE_ID_REALTEK_8029},
 	{PCI_VENDOR_ID_WINBOND2,	PCI_DEVICE_ID_WINBOND2_89C940},
 	{PCI_VENDOR_ID_COMPEX,		PCI_DEVICE_ID_COMPEX_RL2000},
+	{PCI_VENDOR_ID_KTI,		PCI_DEVICE_ID_KTI_ET32P2},
 	{0,}
 };
 #endif
diff -u --recursive --new-file v2.1.26/linux/drivers/net/ni52.c linux/drivers/net/ni52.c
--- v2.1.26/linux/drivers/net/ni52.c	Sun Feb  2 05:18:40 1997
+++ linux/drivers/net/ni52.c	Fri Feb 21 14:58:34 1997
@@ -164,7 +164,7 @@
 #define DELAY_18(); { __delay( (loops_per_sec>>18)+1 ); }
 
 /* wait for command with timeout: */
-#define WAIT_4_SCB_CMD() 
+#define WAIT_4_SCB_CMD() \
 { int i; \
   for(i=0;i<16384;i++) { \
     if(!p->scb->cmd_cuc) break; \
diff -u --recursive --new-file v2.1.26/linux/drivers/net/sdla.c linux/drivers/net/sdla.c
--- v2.1.26/linux/drivers/net/sdla.c	Sun Feb  2 05:18:41 1997
+++ linux/drivers/net/sdla.c	Fri Feb 21 14:58:34 1997
@@ -432,7 +432,7 @@
 	cmd_buf->flags = flags;
 
 	if (inbuf)
-		amemcpy(cmd_buf->data, inbuf, inlen);
+		memcpy(cmd_buf->data, inbuf, inlen);
 
 	cmd_buf->length = inlen;
 
@@ -1103,7 +1103,7 @@
 {
 	struct frad_local *flp;
 	struct conf_data  data;
-	int               i, err;
+	int               i;
 	short             size;
 
 	if (dev->type == 0xFFFF)
diff -u --recursive --new-file v2.1.26/linux/drivers/net/sm_afsk1200.h linux/drivers/net/sm_afsk1200.h
--- v2.1.26/linux/drivers/net/sm_afsk1200.h	Sat Dec 21 09:08:38 1996
+++ linux/drivers/net/sm_afsk1200.h	Wed Dec 31 16:00:00 1969
@@ -1,304 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_afsk1200.h  -- soundcard radio modem driver, 1200 baud AFSK modem
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#include <linux/config.h>
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk12 {
-	unsigned int shreg;
-	unsigned int bit_pll;
-	unsigned char last_sample;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-	unsigned char last_rxbit;
-	union {
-		signed char c[8];
-		float f[8];
-	} filt;
-};
-
-struct mod_state_afsk12 {
-	unsigned int shreg;
-	unsigned char tx_bit;
-	unsigned int bit_pll;
-};
-
-#define DEMOD_STATE ((struct demod_state_afsk12 *)(&sm->d))
-#define MOD_STATE ((struct mod_state_afsk12 *)(&sm->m))
-
-/* --------------------------------------------------------------------- */
-
-static const char sinetab[] = {
-	 128,  140,  152,  164,  176,  187,  198,  208,
-	 217,  226,  233,  240,  245,  249,  252,  254,
-	 255,  254,  252,  249,  245,  240,  233,  226,
-	 217,  208,  198,  187,  176,  164,  152,  140,
-	 128,  116,  104,   92,   80,   69,   58,   48,
-	  39,   30,   23,   16,   11,    7,    4,    2,
-	   1,    2,    4,    7,   11,   16,   23,   30,
-	  39,   48,   58,   69,   80,   92,  104,  116
-};
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	static const int dds_inc[2] = { 8192, 15019 };
-	int j, k;
-
-	for (; buflen >= 8; buflen -= 8) {
-		if (MOD_STATE->shreg <= 1)
-			MOD_STATE->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-		MOD_STATE->tx_bit = (MOD_STATE->tx_bit ^ 
-				       (!(MOD_STATE->shreg & 1))) & 1;
-		MOD_STATE->shreg >>= 1;
-		k = dds_inc[MOD_STATE->tx_bit & 1];
-		for (j = 0; j < 8; j++) {
-			*buf++ = sinetab[(MOD_STATE->bit_pll >> 10) & 0x3f];
-			MOD_STATE->bit_pll += k;
-		}
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * should eventually move to an asm header file
- */
-#if defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686))
-
-#define ENV_STORAGE unsigned char fpu_save[108];
-
-#define ENV_SAVE asm("fsave %0;\n\tfninit;\n\t" : "=m" (*fpu_save) : : "memory");
-#define ENV_RESTORE asm("frstor %0;\n\t" : : "m" (*fpu_save));
-
-static const float tx_lo_i_f[] = { 
-	1.000000, 0.707107, 0.000000, -0.707107, -1.000000, -0.707107, -0.000000, 0.707107 
-};
-
-static const float tx_lo_q_f[] = { 
-	0.000000, 0.707107, 1.000000, 0.707107, 0.000000, -0.707107, -1.000000, -0.707107 
-};
-
-static const float tx_hi_i_f[] = { 
-	1.000000, 0.130526, -0.965926, -0.382683, 0.866025, 0.608761, -0.707107, -0.793353 
-};
-
-static const float tx_hi_q_f[] = { 
-	0.000000, 0.991445, 0.258819, -0.923880, -0.500000, 0.793353, 0.707107, -0.608761 
-};
-
-static inline float convolution8(const float *st, const float *coeff)
-{
-	float f;
-
-	/*
-	 * from Phil Karn, KA9Q's home page
-	 */
-	asm volatile ("flds (%1);\n\t"
-	    "fmuls (%2);\n\t"
-	    "flds 4(%1);\n\t"
-	    "fmuls 4(%2);\n\t"
-	    "flds 8(%1);\n\t"
-	    "fmuls 8(%2);\n\t"
-	    "fxch %%st(2);\n\t"
-	    "faddp;\n\t"
-	    "flds 12(%1);\n\t"
-	    "fmuls 12(%2);\n\t"
-	    "fxch %%st(2);\n\t"
-	    "faddp;\n\t"
-	    "flds 16(%1);\n\t"
-	    "fmuls 16(%2);\n\t"
-	    "fxch %%st(2);\n\t"
-	    "faddp;\n\t"
-	    "flds 20(%1);\n\t"
-	    "fmuls 20(%2);\n\t"
-	    "fxch %%st(2);\n\t"
-	    "faddp;\n\t"
-	    "flds 24(%1);\n\t"
-	    "fmuls 24(%2);\n\t"
-	    "fxch %%st(2);\n\t"
-	    "faddp;\n\t"
-	    "flds 28(%1);\n\t"
-	    "fmuls 28(%2);\n\t"
-	    "fxch %%st(2);\n\t"
-	    "faddp;\n\t"
-	    "faddp;\n\t"
-	    "fmul %%st(0),%%st;\n\t" :
-	    "=t" (f) :
-	    "r" (st),
-	    "r" (coeff) : "memory");
-
-	return f;
-}
-
-static inline int do_filter_1200(struct sm_state *sm, unsigned char newval)
-{
-	float sum;
-
-	memmove(DEMOD_STATE->filt.f+1, DEMOD_STATE->filt.f,
-		sizeof(DEMOD_STATE->filt.f) - sizeof(DEMOD_STATE->filt.f[0]));
-	DEMOD_STATE->filt.f[0] = (((int)newval)-0x80);
-
-	sum = convolution8(DEMOD_STATE->filt.f, tx_lo_i);
-	sum += convolution8(DEMOD_STATE->filt.f, tx_lo_q);
-	sum -= convolution8(DEMOD_STATE->filt.f, tx_hi_i);
-	sum -= convolution8(DEMOD_STATE->filt.f, tx_hi_q);
-	return sum;
-}
-
-#else /* defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
-
-#define ENV_STORAGE 
-#define ENV_SAVE
-#define ENV_RESTORE
-
-static const char tx_lo_i[] = { 127, 89, 0, -89, -127, -89, 0, 89 };
-static const char tx_lo_q[] = { 0, 89, 127, 89, 0, -89, -127, -89 };
-static const char tx_hi_i[] = { 127, 16, -122, -48, 109, 77, -89, -100 };
-static const char tx_hi_q[] = { 0, 125, 32, -117, -63, 100, 89, -77 };
-
-static inline void datamove8(signed char *st, unsigned char newval)
-{
-	memmove(st+1, st, 7);
-	*st = newval - 0x80;
-}
-
-static inline int convolution8(const signed char *st, const signed char *coeff)
-{
-	int sum = (st[0] * coeff[0]);
-	
-	sum += (st[1] * coeff[1]);
-	sum += (st[2] * coeff[2]);
-	sum += (st[3] * coeff[3]);
-	sum += (st[4] * coeff[4]);
-	sum += (st[5] * coeff[5]);
-	sum += (st[6] * coeff[6]);
-	sum += (st[7] * coeff[7]);
-
-	sum >>= 7;
-	return sum * sum;
-}
-
-static inline int do_filter_1200(struct sm_state *sm, unsigned char newval)
-{
-	int sum;
-
-	datamove8(DEMOD_STATE->filt.c, newval);
-	
-	sum = convolution8(DEMOD_STATE->filt.c, tx_lo_i);
-	sum += convolution8(DEMOD_STATE->filt.c, tx_lo_q);
-	sum -= convolution8(DEMOD_STATE->filt.c, tx_hi_i);
-	sum -= convolution8(DEMOD_STATE->filt.c, tx_hi_q);
-	return sum;
-}
-
-#endif /* defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
-
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	int j;
-	int sum;
-	unsigned char newsample;
-	ENV_STORAGE;
-
-	ENV_SAVE;
-	for (; buflen > 0; buflen--, buf++) {
-		sum = do_filter_1200(sm, *buf);
-		DEMOD_STATE->dcd_shreg <<= 1;
-		DEMOD_STATE->bit_pll += 0x2000;
-		newsample = (sum > 0);
-		if (DEMOD_STATE->last_sample ^ newsample) {
-			DEMOD_STATE->last_sample = newsample;
-			DEMOD_STATE->dcd_shreg |= 1;
-			DEMOD_STATE->bit_pll += pll_corr
-				[DEMOD_STATE->bit_pll < 0x9000];
-			j = 4 * hweight8(DEMOD_STATE->dcd_shreg & 0x38)
-				- hweight16(DEMOD_STATE->dcd_shreg & 0x7c0);
-			DEMOD_STATE->dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, DEMOD_STATE->last_sample);
-		if ((--DEMOD_STATE->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (DEMOD_STATE->dcd_sum0 + 
-						   DEMOD_STATE->dcd_sum1 + 
-						   DEMOD_STATE->dcd_sum2) < 0);
-			DEMOD_STATE->dcd_sum2 = DEMOD_STATE->dcd_sum1;
-			DEMOD_STATE->dcd_sum1 = DEMOD_STATE->dcd_sum0;
-			DEMOD_STATE->dcd_sum0 = 2; /* slight bias */
-			DEMOD_STATE->dcd_time = 120;
-		}
-		if (DEMOD_STATE->bit_pll >= 0x10000) {
-			DEMOD_STATE->bit_pll &= 0xffff;
-			DEMOD_STATE->shreg >>= 1;
-			DEMOD_STATE->shreg |= (!(DEMOD_STATE->last_rxbit ^
-						 DEMOD_STATE->last_sample)) << 16;
-			DEMOD_STATE->last_rxbit = DEMOD_STATE->last_sample;
-			diag_trigger(sm);
-			if (DEMOD_STATE->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, DEMOD_STATE->shreg >> 1);
-				DEMOD_STATE->shreg = 0x10000;
-			}
-		}
-		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
-	}
-	ENV_RESTORE;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_1200(struct sm_state *sm)
-{
-       	DEMOD_STATE->dcd_time = 120;
-	DEMOD_STATE->dcd_sum0 = 2;	
-}
-
-/* --------------------------------------------------------------------- */
-
-static const struct modem_tx_info afsk1200_tx = {
-	NEXT_TX_INFO, "afsk1200", sizeof(struct mod_state_afsk12), 9600, 1200, 8, 
-	modulator_1200, NULL
-};
-#undef NEXT_TX_INFO
-#define NEXT_TX_INFO (&afsk1200_tx)
-
-static const struct modem_rx_info afsk1200_rx = {
-	NEXT_RX_INFO, "afsk1200", sizeof(struct demod_state_afsk12), 9600, 1200, 8, 8, 
-	demodulator_1200, demod_init_1200
-};
-#undef NEXT_RX_INFO
-#define NEXT_RX_INFO (&afsk1200_rx)
-
-/* --------------------------------------------------------------------- */
-
-#undef DEMOD_STATE
-#undef MOD_STATE
diff -u --recursive --new-file v2.1.26/linux/drivers/net/sm_fsk9600.h linux/drivers/net/sm_fsk9600.h
--- v2.1.26/linux/drivers/net/sm_fsk9600.h	Fri Dec 20 03:16:51 1996
+++ linux/drivers/net/sm_fsk9600.h	Wed Dec 31 16:00:00 1969
@@ -1,552 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_fsk9600.h  --  soundcard radio modem driver, 
- *                        9600 baud G3RUH compatible FSK modem
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_fsk96 {
-	unsigned int shreg;
-	unsigned long descram;
-	unsigned int bit_pll;
-	unsigned char last_sample;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-};
-
-struct mod_state_fsk96 {
-	unsigned int shreg;
-	unsigned long scram;
-	unsigned char tx_bit;
-};
-
-#define DEMOD_STATE ((struct demod_state_fsk96 *)(&sm->d))
-#define MOD_STATE ((struct mod_state_fsk96 *)(&sm->m))
-
-/* --------------------------------------------------------------------- */
-
-#define DESCRAM_TAP1 0x20000
-#define DESCRAM_TAP2 0x01000
-#define DESCRAM_TAP3 0x00001
-
-#define DESCRAM_TAPSH1 17
-#define DESCRAM_TAPSH2 12
-#define DESCRAM_TAPSH3 0
-
-#define SCRAM_TAP1 0x20000 /* X^17 */
-#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static unsigned char tx_filter_9k6_4[] = {
-	  65,  66,  63,  63,  76,  76,  73,  73,
-	  54,  54,  51,  51,  64,  65,  61,  62,
-	 214, 214, 211, 211, 224, 225, 221, 222,
-	 202, 202, 199, 200, 213, 213, 210, 210,
-	  38,  38,  35,  35,  48,  49,  45,  46,
-	  26,  27,  23,  24,  37,  37,  34,  34,
-	 186, 187, 183, 184, 197, 197, 194, 194,
-	 175, 175, 172, 172, 185, 185, 182, 182,
-	  73,  74,  70,  71,  84,  84,  81,  81,
-	  62,  62,  59,  59,  72,  72,  69,  69,
-	 221, 222, 218, 219, 232, 232, 229, 229,
-	 210, 210, 207, 207, 220, 221, 217, 218,
-	  46,  46,  43,  43,  56,  56,  53,  53,
-	  34,  34,  31,  31,  44,  45,  41,  42,
-	 194, 194, 191, 191, 204, 205, 201, 202,
-	 182, 183, 179, 180, 193, 193, 190, 190,
-	  65,  65,  62,  62,  75,  76,  72,  73,
-	  53,  54,  50,  51,  64,  64,  61,  61,
-	 213, 214, 210, 211, 224, 224, 221, 221,
-	 202, 202, 199, 199, 212, 212, 209, 209,
-	  37,  38,  34,  35,  48,  48,  45,  45,
-	  26,  26,  23,  23,  36,  37,  33,  34,
-	 186, 186, 183, 183, 196, 196, 193, 193,
-	 174, 174, 171, 171, 184, 185, 181, 182,
-	  73,  73,  70,  70,  83,  83,  80,  80,
-	  61,  61,  58,  58,  71,  72,  68,  69,
-	 221, 221, 218, 218, 231, 232, 228, 229,
-	 209, 210, 206, 207, 220, 220, 217, 217,
-	  45,  45,  42,  42,  55,  56,  53,  53,
-	  33,  34,  30,  31,  44,  44,  41,  41,
-	 193, 194, 190, 191, 204, 204, 201, 201,
-	 182, 182, 179, 179, 192, 192, 189, 190,
-	  65,  66,  62,  63,  67,  68,  64,  64,
-	 103, 104, 100, 101, 105, 106, 101, 102,
-	 168, 169, 164, 165, 169, 170, 166, 167,
-	 205, 206, 202, 203, 207, 208, 204, 205,
-	  49,  50,  46,  46,  51,  51,  47,  48,
-	  87,  88,  83,  84,  88,  89,  85,  86,
-	 151, 152, 148, 149, 153, 154, 149, 150,
-	 189, 190, 186, 187, 191, 192, 187, 188,
-	  66,  67,  63,  64,  68,  69,  64,  65,
-	 104, 105, 100, 101, 105, 106, 102, 103,
-	 168, 169, 165, 166, 170, 171, 167, 167,
-	 206, 207, 203, 204, 208, 209, 204, 205,
-	  50,  51,  46,  47,  51,  52,  48,  49,
-	  87,  88,  84,  85,  89,  90,  85,  86,
-	 152, 153, 149, 149, 154, 154, 150, 151,
-	 190, 191, 186, 187, 191, 192, 188, 189,
-	  66,  67,  63,  64,  68,  69,  64,  65,
-	 104, 105, 101, 101, 106, 106, 102, 103,
-	 169, 170, 165, 166, 170, 171, 167, 168,
-	 206, 207, 203, 204, 208, 209, 204, 205,
-	  50,  51,  46,  47,  51,  52,  48,  49,
-	  88,  88,  84,  85,  89,  90,  86,  87,
-	 152, 153, 149, 150, 154, 155, 150, 151,
-	 190, 191, 186, 187, 191, 192, 188, 189,
-	  67,  68,  63,  64,  68,  69,  65,  66,
-	 105, 106, 101, 102, 106, 107, 103, 104,
-	 169, 170, 166, 167, 171, 172, 167, 168,
-	 207, 208, 204, 204, 209, 209, 205, 206,
-	  50,  51,  47,  48,  52,  53,  49,  50,
-	  88,  89,  85,  86,  90,  91,  86,  87,
-	 153, 154, 149, 150, 154, 155, 151, 152,
-	 191, 191, 187, 188, 192, 193, 189, 190,
-	  65,  66,  66,  67,  49,  50,  50,  50,
-	 168, 169, 168, 169, 151, 152, 152, 153,
-	 103, 104, 104, 105,  87,  88,  87,  88,
-	 205, 206, 206, 207, 189, 190, 190, 191,
-	  67,  68,  68,  68,  51,  51,  51,  52,
-	 169, 170, 170, 171, 153, 154, 154, 154,
-	 105, 106, 105, 106,  88,  89,  89,  90,
-	 207, 208, 208, 209, 191, 191, 191, 192,
-	  62,  63,  63,  63,  46,  46,  46,  47,
-	 164, 165, 165, 166, 148, 149, 149, 149,
-	 100, 101, 100, 101,  83,  84,  84,  85,
-	 202, 203, 203, 204, 186, 186, 186, 187,
-	  64,  64,  64,  65,  47,  48,  48,  49,
-	 166, 167, 167, 167, 149, 150, 150, 151,
-	 101, 102, 102, 103,  85,  86,  85,  86,
-	 204, 204, 204, 205, 187, 188, 188, 189,
-	  66,  67,  67,  68,  50,  51,  51,  51,
-	 169, 170, 169, 170, 152, 153, 153, 154,
-	 104, 105, 105, 106,  88,  88,  88,  89,
-	 206, 207, 207, 208, 190, 191, 191, 191,
-	  68,  69,  69,  69,  51,  52,  52,  53,
-	 170, 171, 171, 172, 154, 155, 154, 155,
-	 106, 106, 106, 107,  89,  90,  90,  91,
-	 208, 209, 209, 209, 192, 192, 192, 193,
-	  63,  64,  64,  64,  46,  47,  47,  48,
-	 165, 166, 166, 167, 149, 150, 149, 150,
-	 101, 101, 101, 102,  84,  85,  85,  86,
-	 203, 204, 204, 204, 187, 187, 187, 188,
-	  64,  65,  65,  66,  48,  49,  49,  50,
-	 167, 168, 167, 168, 150, 151, 151, 152,
-	 102, 103, 103, 104,  86,  87,  86,  87,
-	 205, 205, 205, 206, 188, 189, 189, 190,
-	  65,  65,  73,  73,  38,  37,  46,  45,
-	 214, 213, 221, 221, 186, 186, 194, 193,
-	  54,  53,  62,  61,  26,  26,  34,  33,
-	 202, 202, 210, 209, 175, 174, 182, 182,
-	  76,  75,  84,  83,  48,  48,  56,  55,
-	 224, 224, 232, 231, 197, 196, 204, 204,
-	  64,  64,  72,  71,  37,  36,  44,  44,
-	 213, 212, 220, 220, 185, 184, 193, 192,
-	  63,  62,  70,  70,  35,  34,  43,  42,
-	 211, 210, 218, 218, 183, 183, 191, 190,
-	  51,  50,  59,  58,  23,  23,  31,  30,
-	 199, 199, 207, 206, 172, 171, 179, 179,
-	  73,  72,  81,  80,  45,  45,  53,  53,
-	 221, 221, 229, 228, 194, 193, 201, 201,
-	  61,  61,  69,  68,  34,  33,  41,  41,
-	 210, 209, 217, 217, 182, 181, 190, 189,
-	  66,  65,  74,  73,  38,  38,  46,  45,
-	 214, 214, 222, 221, 187, 186, 194, 194,
-	  54,  54,  62,  61,  27,  26,  34,  34,
-	 202, 202, 210, 210, 175, 174, 183, 182,
-	  76,  76,  84,  83,  49,  48,  56,  56,
-	 225, 224, 232, 232, 197, 196, 205, 204,
-	  65,  64,  72,  72,  37,  37,  45,  44,
-	 213, 212, 221, 220, 185, 185, 193, 192,
-	  63,  62,  71,  70,  35,  35,  43,  42,
-	 211, 211, 219, 218, 184, 183, 191, 191,
-	  51,  51,  59,  58,  24,  23,  31,  31,
-	 200, 199, 207, 207, 172, 171, 180, 179,
-	  73,  73,  81,  80,  46,  45,  53,  53,
-	 222, 221, 229, 229, 194, 193, 202, 201,
-	  62,  61,  69,  69,  34,  34,  42,  41,
-	 210, 209, 218, 217, 182, 182, 190, 190
-};
-
-static unsigned char tx_filter_9k6_5[] = {
-	  78,  78,  75,  76,  85,  85,  82,  83,
-	  71,  72,  69,  69,  78,  78,  76,  76,
-	 193, 193, 190, 191, 199, 200, 197, 198,
-	 186, 187, 184, 184, 193, 193, 190, 191,
-	  58,  59,  56,  57,  65,  66,  63,  63,
-	  52,  52,  49,  50,  58,  59,  56,  57,
-	 173, 174, 171, 171, 180, 180, 177, 178,
-	 166, 167, 164, 165, 173, 174, 171, 171,
-	  85,  86,  83,  83,  92,  92,  89,  90,
-	  79,  79,  76,  77,  85,  86,  83,  83,
-	 200, 200, 198, 198, 207, 207, 204, 205,
-	 193, 194, 191, 191, 200, 201, 198, 198,
-	  66,  66,  63,  64,  72,  73,  70,  70,
-	  59,  60,  57,  57,  66,  66,  63,  64,
-	 180, 181, 178, 179, 187, 188, 185, 185,
-	 174, 174, 171, 172, 180, 181, 178, 179,
-	  76,  77,  74,  75,  83,  84,  81,  81,
-	  70,  70,  67,  68,  76,  77,  74,  75,
-	 191, 192, 189, 189, 198, 198, 195, 196,
-	 185, 185, 182, 183, 191, 192, 189, 189,
-	  57,  57,  54,  55,  64,  64,  61,  62,
-	  50,  51,  48,  48,  57,  57,  55,  55,
-	 172, 172, 169, 170, 178, 179, 176, 177,
-	 165, 166, 163, 163, 172, 172, 169, 170,
-	  84,  84,  81,  82,  90,  91,  88,  89,
-	  77,  78,  75,  75,  84,  84,  81,  82,
-	 198, 199, 196, 197, 205, 206, 203, 203,
-	 192, 192, 189, 190, 198, 199, 196, 197,
-	  64,  65,  62,  62,  71,  71,  68,  69,
-	  57,  58,  55,  56,  64,  65,  62,  62,
-	 179, 179, 177, 177, 186, 186, 183, 184,
-	 172, 173, 170, 170, 179, 180, 177, 177,
-	  78,  79,  76,  77,  79,  79,  77,  77,
-	  99, 100,  97,  98, 100, 100,  98,  98,
-	 171, 172, 169, 170, 172, 173, 170, 171,
-	 192, 193, 190, 191, 193, 194, 191, 192,
-	  59,  60,  57,  58,  60,  61,  58,  59,
-	  81,  81,  79,  79,  81,  82,  79,  80,
-	 153, 153, 151, 151, 153, 154, 151, 152,
-	 174, 175, 172, 173, 175, 175, 173, 173,
-	  82,  83,  80,  81,  83,  83,  81,  81,
-	 103, 104, 101, 102, 104, 105, 102, 103,
-	 175, 176, 173, 174, 176, 177, 174, 175,
-	 197, 197, 195, 195, 197, 198, 195, 196,
-	  64,  64,  62,  62,  64,  65,  62,  63,
-	  85,  85,  83,  83,  85,  86,  83,  84,
-	 157, 158, 155, 156, 158, 158, 156, 156,
-	 178, 179, 176, 177, 179, 179, 177, 177,
-	  78,  78,  76,  76,  78,  79,  76,  77,
-	  99,  99,  97,  97,  99, 100,  97,  98,
-	 171, 172, 169, 170, 172, 172, 170, 170,
-	 192, 193, 190, 191, 193, 193, 191, 191,
-	  59,  60,  57,  58,  60,  60,  58,  58,
-	  80,  81,  78,  79,  81,  82,  79,  80,
-	 152, 153, 150, 151, 153, 154, 151, 152,
-	 174, 174, 172, 172, 174, 175, 172, 173,
-	  82,  82,  80,  80,  82,  83,  80,  81,
-	 103, 104, 101, 102, 104, 104, 102, 102,
-	 175, 176, 173, 174, 176, 176, 174, 174,
-	 196, 197, 194, 195, 197, 198, 195, 196,
-	  63,  64,  61,  62,  64,  65,  62,  63,
-	  84,  85,  82,  83,  85,  86,  83,  84,
-	 157, 157, 155, 155, 157, 158, 155, 156,
-	 178, 178, 176, 176, 178, 179, 176, 177,
-	  78,  78,  78,  79,  69,  69,  69,  69,
-	 136, 136, 136, 137, 127, 127, 127, 128,
-	 136, 136, 136, 137, 127, 127, 127, 128,
-	 194, 195, 195, 195, 185, 185, 185, 186,
-	  69,  69,  69,  69,  59,  60,  60,  60,
-	 127, 127, 127, 128, 117, 118, 118, 118,
-	 127, 127, 127, 128, 117, 118, 118, 118,
-	 185, 185, 185, 186, 175, 176, 176, 176,
-	  78,  79,  79,  79,  69,  69,  69,  70,
-	 136, 137, 137, 137, 127, 128, 127, 128,
-	 136, 137, 137, 137, 127, 128, 127, 128,
-	 195, 195, 195, 195, 185, 186, 186, 186,
-	  69,  69,  69,  70,  60,  60,  60,  60,
-	 127, 128, 127, 128, 118, 118, 118, 119,
-	 127, 128, 127, 128, 118, 118, 118, 119,
-	 185, 186, 186, 186, 176, 176, 176, 177,
-	  78,  79,  79,  79,  69,  69,  69,  70,
-	 136, 137, 137, 137, 127, 128, 128, 128,
-	 136, 137, 137, 137, 127, 128, 128, 128,
-	 195, 195, 195, 195, 185, 186, 186, 186,
-	  69,  69,  69,  70,  60,  60,  60,  60,
-	 127, 128, 128, 128, 118, 118, 118, 119,
-	 127, 128, 128, 128, 118, 118, 118, 119,
-	 185, 186, 186, 186, 176, 176, 176, 177,
-	  79,  79,  79,  80,  69,  70,  70,  70,
-	 137, 137, 137, 138, 128, 128, 128, 128,
-	 137, 137, 137, 138, 128, 128, 128, 128,
-	 195, 195, 195, 196, 186, 186, 186, 186,
-	  69,  70,  70,  70,  60,  60,  60,  61,
-	 128, 128, 128, 128, 118, 119, 119, 119,
-	 128, 128, 128, 128, 118, 119, 119, 119,
-	 186, 186, 186, 186, 176, 177, 177, 177,
-	  78,  78,  82,  82,  59,  59,  64,  63,
-	 171, 171, 175, 175, 153, 152, 157, 157,
-	  99,  99, 103, 103,  81,  80,  85,  84,
-	 192, 192, 197, 196, 174, 174, 178, 178,
-	  79,  78,  83,  82,  60,  60,  64,  64,
-	 172, 172, 176, 176, 153, 153, 158, 157,
-	 100,  99, 104, 104,  81,  81,  85,  85,
-	 193, 193, 197, 197, 175, 174, 179, 178,
-	  76,  76,  80,  80,  57,  57,  62,  61,
-	 169, 169, 173, 173, 151, 150, 155, 155,
-	  97,  97, 101, 101,  79,  78,  83,  82,
-	 190, 190, 195, 194, 172, 172, 176, 176,
-	  77,  76,  81,  80,  58,  58,  62,  62,
-	 170, 170, 174, 174, 151, 151, 156, 155,
-	  98,  97, 102, 102,  79,  79,  83,  83,
-	 191, 191, 195, 195, 173, 172, 177, 176,
-	  79,  78,  83,  82,  60,  60,  64,  64,
-	 172, 172, 176, 176, 153, 153, 158, 157,
-	 100,  99, 104, 104,  81,  81,  85,  85,
-	 193, 193, 197, 197, 175, 174, 179, 178,
-	  79,  79,  83,  83,  61,  60,  65,  65,
-	 173, 172, 177, 176, 154, 154, 158, 158,
-	 100, 100, 105, 104,  82,  82,  86,  86,
-	 194, 193, 198, 198, 175, 175, 179, 179,
-	  77,  76,  81,  80,  58,  58,  62,  62,
-	 170, 170, 174, 174, 151, 151, 156, 155,
-	  98,  97, 102, 102,  79,  79,  83,  83,
-	 191, 191, 195, 195, 173, 172, 177, 176,
-	  77,  77,  81,  81,  59,  58,  63,  63,
-	 171, 170, 175, 174, 152, 152, 156, 156,
-	  98,  98, 103, 102,  80,  80,  84,  84,
-	 192, 191, 196, 196, 173, 173, 177, 177,
-	  78,  76,  85,  84,  58,  57,  66,  64,
-	 193, 191, 200, 198, 173, 172, 180, 179,
-	  71,  70,  79,  77,  52,  50,  59,  57,
-	 186, 185, 193, 192, 166, 165, 174, 172,
-	  85,  83,  92,  90,  65,  64,  72,  71,
-	 199, 198, 207, 205, 180, 178, 187, 186,
-	  78,  76,  85,  84,  58,  57,  66,  64,
-	 193, 191, 200, 198, 173, 172, 180, 179,
-	  75,  74,  83,  81,  56,  54,  63,  62,
-	 190, 189, 198, 196, 171, 169, 178, 177,
-	  69,  67,  76,  75,  49,  48,  57,  55,
-	 184, 182, 191, 189, 164, 163, 171, 170,
-	  82,  81,  89,  88,  63,  61,  70,  68,
-	 197, 195, 204, 203, 177, 176, 185, 183,
-	  76,  74,  83,  81,  56,  55,  63,  62,
-	 190, 189, 198, 196, 171, 169, 178, 177,
-	  78,  77,  86,  84,  59,  57,  66,  65,
-	 193, 192, 200, 199, 174, 172, 181, 179,
-	  72,  70,  79,  78,  52,  51,  60,  58,
-	 187, 185, 194, 192, 167, 166, 174, 173,
-	  85,  84,  92,  91,  66,  64,  73,  71,
-	 200, 198, 207, 206, 180, 179, 188, 186,
-	  78,  77,  86,  84,  59,  57,  66,  65,
-	 193, 192, 201, 199, 174, 172, 181, 180,
-	  76,  75,  83,  82,  57,  55,  64,  62,
-	 191, 189, 198, 197, 171, 170, 179, 177,
-	  69,  68,  77,  75,  50,  48,  57,  56,
-	 184, 183, 191, 190, 165, 163, 172, 170,
-	  83,  81,  90,  89,  63,  62,  70,  69,
-	 198, 196, 205, 203, 178, 177, 185, 184,
-	  76,  75,  83,  82,  57,  55,  64,  62,
-	 191, 189, 198, 197, 171, 170, 179, 177
-};
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	int j;
-	const unsigned char *cp;
-
-	for (; buflen >= 4; buflen -= 4) {
-		if (MOD_STATE->shreg <= 1)
-			MOD_STATE->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-		MOD_STATE->scram = ((MOD_STATE->scram << 1) |
-				     (MOD_STATE->scram & 1));
-		MOD_STATE->scram ^= (!(MOD_STATE->shreg & 1));
-		MOD_STATE->shreg >>= 1;
-		if (MOD_STATE->scram & (SCRAM_TAP1 << 1))
-			MOD_STATE->scram ^= (SCRAM_TAPN << 1);
-		MOD_STATE->tx_bit = (MOD_STATE->tx_bit << 1) | 
-			(!!(MOD_STATE->scram & (SCRAM_TAP1 << 2)));
-		cp = tx_filter_9k6_4 + (MOD_STATE->tx_bit & 0xff);
-		for (j = 0; j < 4; j++) {
-			*buf++ = *cp;
-			cp += 0x100;
-		}
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		DEMOD_STATE->dcd_shreg <<= 1;
-		DEMOD_STATE->bit_pll += 0x4000;
-		curbit = (*buf >= 0x80);
-		if (DEMOD_STATE->last_sample ^ curbit) {
-			DEMOD_STATE->dcd_shreg |= 1;
-			DEMOD_STATE->bit_pll += pll_corr
-				[DEMOD_STATE->bit_pll < 0xa000];
-			DEMOD_STATE->dcd_sum0 += 8 * 
-				hweight8(DEMOD_STATE->dcd_shreg & 0x0c) - 
-				!!(DEMOD_STATE->dcd_shreg & 0x10);
-		}
-		DEMOD_STATE->last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, DEMOD_STATE->last_sample);
-		if ((--DEMOD_STATE->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (DEMOD_STATE->dcd_sum0 + 
-						   DEMOD_STATE->dcd_sum1 + 
-						   DEMOD_STATE->dcd_sum2) < 0);
-			DEMOD_STATE->dcd_sum2 = DEMOD_STATE->dcd_sum1;
-			DEMOD_STATE->dcd_sum1 = DEMOD_STATE->dcd_sum0;
-			DEMOD_STATE->dcd_sum0 = 2; /* slight bias */
-			DEMOD_STATE->dcd_time = 240;
-		}
-		if (DEMOD_STATE->bit_pll >= 0x10000) {
-			DEMOD_STATE->bit_pll &= 0xffff;
-			DEMOD_STATE->descram = (DEMOD_STATE->descram << 1) | curbit;
-			descx = DEMOD_STATE->descram ^ (DEMOD_STATE->descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			DEMOD_STATE->shreg >>= 1;
-			DEMOD_STATE->shreg |= (!(descx & 1)) << 16;
-			if (DEMOD_STATE->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, DEMOD_STATE->shreg >> 1);
-				DEMOD_STATE->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	int j;
-	const unsigned char *cp;
-
-	for (; buflen >= 5; buflen -= 5) {
-		if (MOD_STATE->shreg <= 1)
-			MOD_STATE->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-		MOD_STATE->scram = ((MOD_STATE->scram << 1) |
-				     (MOD_STATE->scram & 1));
-		MOD_STATE->scram ^= (!(MOD_STATE->shreg & 1));
-		MOD_STATE->shreg >>= 1;
-		if (MOD_STATE->scram & (SCRAM_TAP1 << 1))
-			MOD_STATE->scram ^= (SCRAM_TAPN << 1);
-		MOD_STATE->tx_bit = (MOD_STATE->tx_bit << 1) | 
-			(!!(MOD_STATE->scram & (SCRAM_TAP1 << 2)));
-		cp = tx_filter_9k6_5 + (MOD_STATE->tx_bit & 0xff);
-		for (j = 0; j < 5; j++) {
-			*buf++ = *cp;
-			cp += 0x100;
-		}
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
-{
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		DEMOD_STATE->dcd_shreg <<= 1;
-		DEMOD_STATE->bit_pll += 0x3333;
-		curbit = (*buf >= 0x80);
-		if (DEMOD_STATE->last_sample ^ curbit) {
-			DEMOD_STATE->dcd_shreg |= 1;
-			DEMOD_STATE->bit_pll += pll_corr
-				[DEMOD_STATE->bit_pll < 0x9999];
-			DEMOD_STATE->dcd_sum0 += 16 * 
-				hweight8(DEMOD_STATE->dcd_shreg & 0x0c) - 
-				hweight8(DEMOD_STATE->dcd_shreg & 0x70);
-		}
-		DEMOD_STATE->last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, DEMOD_STATE->last_sample);
-		if ((--DEMOD_STATE->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (DEMOD_STATE->dcd_sum0 + 
-						   DEMOD_STATE->dcd_sum1 + 
-						   DEMOD_STATE->dcd_sum2) < 0);
-			DEMOD_STATE->dcd_sum2 = DEMOD_STATE->dcd_sum1;
-			DEMOD_STATE->dcd_sum1 = DEMOD_STATE->dcd_sum0;
-			DEMOD_STATE->dcd_sum0 = 2; /* slight bias */
-			DEMOD_STATE->dcd_time = 240;
-		}
-		if (DEMOD_STATE->bit_pll >= 0x10000) {
-			DEMOD_STATE->bit_pll &= 0xffff;
-			DEMOD_STATE->descram = (DEMOD_STATE->descram << 1) | curbit;
-			descx = DEMOD_STATE->descram ^ (DEMOD_STATE->descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			DEMOD_STATE->shreg >>= 1;
-			DEMOD_STATE->shreg |= (!(descx & 1)) << 16;
-			if (DEMOD_STATE->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, DEMOD_STATE->shreg >> 1);
-				DEMOD_STATE->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_9600(struct sm_state *sm)
-{
-	DEMOD_STATE->dcd_time = 240;
-	DEMOD_STATE->dcd_sum0 = 2;	
-}
-
-/* --------------------------------------------------------------------- */
-
-static const struct modem_tx_info fsk9600_4_tx = {
-	NEXT_TX_INFO, "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600, 4,
-	modulator_9600_4, NULL
-};
-#undef NEXT_TX_INFO
-#define NEXT_TX_INFO (&fsk9600_4_tx)
-
-static const struct modem_rx_info fsk9600_4_rx = {
-	NEXT_RX_INFO, "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 4, 4,
-	demodulator_9600_4, demod_init_9600
-};
-#undef NEXT_RX_INFO
-#define NEXT_RX_INFO (&fsk9600_4_rx)
-
-/* --------------------------------------------------------------------- */
-
-static const struct modem_tx_info fsk9600_5_tx = {
-	NEXT_TX_INFO, "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600, 5, 
-	modulator_9600_5, NULL
-};
-#undef NEXT_TX_INFO
-#define NEXT_TX_INFO (&fsk9600_5_tx)
-
-static const struct modem_rx_info fsk9600_5_rx = {
-	NEXT_RX_INFO, "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 5, 5, 
-	demodulator_9600_5, demod_init_9600
-};
-#undef NEXT_RX_INFO
-#define NEXT_RX_INFO (&fsk9600_5_rx)
-
-/* --------------------------------------------------------------------- */
-
-#undef DEMOD_STATE
-#undef MOD_STATE
diff -u --recursive --new-file v2.1.26/linux/drivers/net/sm_sbc.h linux/drivers/net/sm_sbc.h
--- v2.1.26/linux/drivers/net/sm_sbc.h	Fri Dec 20 03:16:51 1996
+++ linux/drivers/net/sm_sbc.h	Wed Dec 31 16:00:00 1969
@@ -1,482 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_sbc.h  -- soundcard radio modem driver soundblaster hardware driver
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-/* --------------------------------------------------------------------- */
-
-struct sc_state_sbc {
-	unsigned char revhi, revlo;
-	unsigned char fmt[2];
-	unsigned int dmabuflen;
-	unsigned char *dmabuf;
-	unsigned char dmabufidx;
-	unsigned char ptt;
-};
-
-#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
-
-/* --------------------------------------------------------------------- */
-/* 
- * the sbc converter's registers 
- */
-#define DSP_RESET(iobase)        (iobase+0x6)
-#define DSP_READ_DATA(iobase)    (iobase+0xa)
-#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
-#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
-#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
-#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
-#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
-#define SBC_EXTENT               16
-
-/* --------------------------------------------------------------------- */
-/*
- * SBC commands
- */
-#define SBC_OUTPUT             0x14
-#define SBC_INPUT              0x24
-#define SBC_BLOCKSIZE          0x48
-#define SBC_HI_OUTPUT          0x91 
-#define SBC_HI_INPUT           0x99 
-#define SBC_LO_OUTPUT_AUTOINIT 0x1c
-#define SBC_LO_INPUT_AUTOINIT  0x2c
-#define SBC_HI_OUTPUT_AUTOINIT 0x90 
-#define SBC_HI_INPUT_AUTOINIT  0x98
-#define SBC_IMMED_INT          0xf2
-#define SBC_GET_REVISION       0xe1
-#define ESS_GET_REVISION       0xe7
-#define SBC_SPEAKER_ON         0xd1
-#define SBC_SPEAKER_OFF        0xd3
-#define SBC_DMA_ON             0xd0
-#define SBC_DMA_OFF            0xd4
-#define SBC_SAMPLE_RATE        0x40
-#define SBC_MONO_8BIT          0xa0
-#define SBC_MONO_16BIT         0xa4
-#define SBC_STEREO_8BIT        0xa8
-#define SBC_STEREO_16BIT       0xac
-
-#define SBC4_OUT8_AI           0xc6
-#define SBC4_IN8_AI            0xce
-#define SBC4_MODE_UNS_MONO     0x00
-
-/* --------------------------------------------------------------------- */
-
-static int inline reset_dsp(struct device *dev)
-{
-	int i;
-
-	outb(1, DSP_RESET(dev->base_addr));
-	for (i = 0; i < 0x100; i++)
-		SLOW_DOWN_IO;
-	outb(0, DSP_RESET(dev->base_addr));
-	for (i = 0; i < 0xffff; i++)
-		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
-			if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
-				return 1;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline write_dsp(struct device *dev, unsigned char data)
-{
-	int i;
-	
-	for (i = 0; i < 0xffff; i++)
-		if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
-			outb(data, DSP_WRITE_DATA(dev->base_addr));
-			return;
-		}
-}
-
-/* --------------------------------------------------------------------- */
-
-static int inline read_dsp(struct device *dev, unsigned char *data)
-{
-	int i;
-
-	if (!data)
-		return 0;
-	for (i = 0; i < 0xffff; i++) 
-		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
-			*data = inb(DSP_READ_DATA(dev->base_addr));
-			return 1;
-		}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline sbc_int_ack(struct device *dev)
-{
-	inb(DSP_DATA_AVAIL(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send)
-{
-        unsigned long flags;
-        static const unsigned char sbcmode[2][2] = {
-		{ SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, 
-		{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }
-	};
-	static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
-        static const unsigned char dmamode[2] = { 
-		DMA_MODE_READ | DMA_MODE_AUTOINIT, DMA_MODE_WRITE  | DMA_MODE_AUTOINIT
-	};
-	static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
-	unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf);
-
-	send = !!send;
-        if (!reset_dsp(dev)) {
-                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
-                return;
-        }
-        if ((dmabufaddr & 0xffff) + SCSTATE->dmabuflen > 0x10000)
-                panic("sm: DMA buffer violates DMA boundary!");
-        save_flags(flags);
-        cli();
-        sbc_int_ack(dev);
-        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
-        write_dsp(dev, SCSTATE->fmt[send]);
-        write_dsp(dev, sbcskr[send]); 
-        disable_dma(dev->dma);
-        clear_dma_ff(dev->dma);
-        set_dma_mode(dev->dma, dmamode[send]);
-        set_dma_addr(dev->dma, dmabufaddr);
-        set_dma_count(dev->dma, SCSTATE->dmabuflen);
-        enable_dma(dev->dma);
-        sbc_int_ack(dev);
-	if (SCSTATE->revhi >= 4) {
-		write_dsp(dev, sbc4mode[send]);
-		write_dsp(dev, SBC4_MODE_UNS_MONO);
-		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
-		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
-	} else {
-		write_dsp(dev, SBC_BLOCKSIZE);
-		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
-		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
-		write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
-		/* hispeed mode if sample rate > 13kHz */
-	}
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	unsigned char new_ptt;
-	unsigned char *buf;
-
-	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	new_ptt = hdlcdrv_ptt(&sm->hdrv);
- 	sbc_int_ack(dev);
- 	buf = SCSTATE->dmabuf;
-	if (SCSTATE->dmabufidx)
-		buf += SCSTATE->dmabuflen/2;
-	SCSTATE->dmabufidx = !SCSTATE->dmabufidx;
-	sm_int_freq(sm);
-	sti();
-	if (new_ptt && !SCSTATE->ptt) {
-		/* starting to transmit */
-		disable_dma(dev->dma);
- 		SCSTATE->dmabufidx = 0;
-		time_exec(sm->debug_vals.demod_cyc, 
-			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf, 
-						 SCSTATE->dmabuflen/2));
- 		setup_dma_dsp(dev, sm, 1);
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf + 
-						 SCSTATE->dmabuflen/2,
-						 SCSTATE->dmabuflen/2));
-	} else if (SCSTATE->ptt == 1 && !new_ptt) {
-		/* stopping transmission */
-		disable_dma(dev->dma);
- 		SCSTATE->dmabufidx = 0;
-		setup_dma_dsp(dev, sm, 0);
-		SCSTATE->ptt = 0;
-	} else if (SCSTATE->ptt) {
-                SCSTATE->ptt--;
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator(sm, buf, SCSTATE->dmabuflen/2));
-        } else {
-		time_exec(sm->debug_vals.demod_cyc, 
-			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
-		hdlcdrv_arbitrate(dev, &sm->hdrv);
-        }
-        if (new_ptt)
-                SCSTATE->ptt = 2;
-	output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_open(struct device *dev, struct sm_state *sm) 
-{
-	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
-		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
-		       sizeof(struct sc_state_sbc), sizeof(sm->m));
-		return -ENODEV;
-	}
-	if (!dev || !sm)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, SBC_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (!reset_dsp(dev))
-		return -ENODEV;
-	write_dsp(dev, SBC_GET_REVISION);
-	if (!read_dsp(dev, &SCSTATE->revhi) || 
-	    !read_dsp(dev, &SCSTATE->revlo))
-		return -ENODEV;
-	if (SCSTATE->revhi < 2) {
-		printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
-		       "rev 2.00 required\n", sm_drvname);
-		return -ENODEV;
-	}
-	if (SCSTATE->revhi < 3 && 
-	    (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
-		printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
-		       "old, at least 3.00 required\n", sm_drvname,
-		       dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
-		return -ENODEV;
-	}
-	/*
-	 * initialize some variables
-	 */
-	if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	SCSTATE->dmabufidx = SCSTATE->ptt = 0;
-
-	memset(&sm->m, 0, sizeof(sm->m));
-	memset(&sm->d, 0, sizeof(sm->d));
-	if (sm->mode_tx->init)
-		sm->mode_tx->init(sm);
-	if (sm->mode_rx->init)
-		sm->mode_rx->init(sm);
-
-	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, 
-			sm->hwdrv->hw_name, dev)) {
-		free_dma(dev->dma);
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
-	setup_dma_dsp(dev, sm, 0);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_close(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-	reset_dsp(dev);	
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	release_region(dev->base_addr, SBC_EXTENT);
-	kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, '.');
-	const struct modem_tx_info *mtp = modem_tx_base;
-	const struct modem_rx_info *mrp;
-	int dv;
-
-	if (!strcmp(mode, "off")) {
-		sm->mode_tx = NULL;
-		sm->mode_rx = NULL;
-		return 0;
-	}
-	if (cp)
-		*cp++ = '\0';
-	else
-		cp = mode;
-	for (; mtp; mtp = mtp->next) {
-		if (mtp->loc_storage > sizeof(sm->m)) {
-			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
-			       sm_drvname, mtp->name, mtp->loc_storage);
-			continue;
-		}
-		if (!mtp->name || strcmp(mtp->name, mode))
-			continue;
-		if (mtp->srate < 5000 || mtp->srate > 44100)
-			continue;
-		for (mrp = modem_rx_base; mrp; mrp = mrp->next) {
-			if (mrp->loc_storage > sizeof(sm->d)) {
-				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
-				       sm_drvname, mrp->name, mrp->loc_storage);
-				continue;
-			}
-			if (mrp->name && !strcmp(mrp->name, cp) &&
-			    mrp->srate >= 5000 && mrp->srate <= 44100) {
-				sm->mode_tx = mtp;
-				sm->mode_rx = mrp;
-				SCSTATE->fmt[0] = 256-((1000000L+mtp->srate/2)/
-							 mrp->srate);
-				SCSTATE->fmt[1] = 256-((1000000L+mtp->srate/2)/
-							 mtp->srate);
-				dv = lcm(sm->mode_tx->dmabuflenmodulo, 
-					 sm->mode_rx->dmabuflenmodulo);
-				SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1;
-				SCSTATE->dmabuflen /= dv;
-				SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */
-	    printk(KERN_DEBUG "sm sbc: modtx %u modrx %u srt %u buflen %u srate %u div %u srate %u div %u\n",
-		   sm->mode_tx->dmabuflenmodulo, sm->mode_rx->dmabuflenmodulo,
-		   sm->mode_rx->srate, SCSTATE->dmabuflen, mrp->srate, SCSTATE->fmt[0], mtp->srate, SCSTATE->fmt[1]);
-				return 0;
-			}
-		}
-	}
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
-		     struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct sm_ioctl bi;
-	unsigned long flags;
-	int i;
-	
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-
-	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
-			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
-			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-
-	switch (bi.cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-	case SMCTL_GETMIXER:
-		i = 0;
-		bi.data.mix.sample_rate = sm->mode_rx->srate;
-		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
-		switch (SCSTATE->revhi) {
-		case 2:
-			bi.data.mix.mixer_type = SM_MIXER_CT1335;
-			break;
-		case 3:
-			bi.data.mix.mixer_type = SM_MIXER_CT1345;
-			break;
-		case 4:
-			bi.data.mix.mixer_type = SM_MIXER_CT1745;
-			break;
-		}
-		if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
-		    bi.data.mix.reg < 0x80) {
-			save_flags(flags);
-			cli();
-			outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
-			bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
-			restore_flags(flags);
-			i = 1;
-		}
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return i;
-		
-	case SMCTL_SETMIXER:
-		if (!suser())
-			return -EACCES;
-		switch (SCSTATE->revhi) {
-		case 2:
-			if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
-				return -EINVAL;
-			break;
-		case 3:
-			if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
-				return -EINVAL;
-			break;
-		case 4:
-			if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
-				return -EINVAL;
-			break;
-		default:
-			return -ENODEV;
-		}
-		if (bi.data.mix.reg >= 0x80)
-			return -EACCES;
-		save_flags(flags);
-		cli();
-		outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
-		outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
-		restore_flags(flags);
-		return 0;
-		
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-struct hardware_info hw_sbc = {
-	NEXT_HW_INFO, "sbc", sizeof(struct sc_state_sbc), 
-	sbc_open, sbc_close, sbc_ioctl, sbc_sethw
-};
-#undef NEXT_HW_INFO
-#define NEXT_HW_INFO (&hw_sbc)
-
-/* --------------------------------------------------------------------- */
-
-#undef SCSTATE
diff -u --recursive --new-file v2.1.26/linux/drivers/net/sm_wss.h linux/drivers/net/sm_wss.h
--- v2.1.26/linux/drivers/net/sm_wss.h	Fri Dec 20 03:16:51 1996
+++ linux/drivers/net/sm_wss.h	Wed Dec 31 16:00:00 1969
@@ -1,910 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_wss.h  -- soundcard radio modem driver, WSS (half duplex) driver
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-/* --------------------------------------------------------------------- */
-
-struct sc_state_wss {
-	unsigned char revwss, revid, revv, revcid;
-	unsigned char fmt[2];
-	unsigned char crystal;
-	unsigned int dmabuflen;
-	unsigned char *dmabuf;
-	unsigned char dmabufidx;
-	unsigned char ptt;
-	/* Full Duplex extensions */
-	unsigned char *dmabuf2;
-};
-
-#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
-
-/* --------------------------------------------------------------------- */
-
-#define WSS_CONFIG(iobase)       (iobase+0)
-#define WSS_STATUS(iobase)       (iobase+3)
-#define WSS_CODEC_IA(iobase)     (iobase+4)
-#define WSS_CODEC_ID(iobase)     (iobase+5)
-#define WSS_CODEC_STATUS(iobase) (iobase+6)
-#define WSS_CODEC_DATA(iobase)   (iobase+7)
-
-#define WSS_EXTENT   8
-
-/* --------------------------------------------------------------------- */
-
-static void write_codec(struct device *dev, unsigned char idx,
-			unsigned char data)
-{
-	int timeout = 900000;
-
-	/* wait until codec ready */
-	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
-		timeout--;
-	outb(idx, WSS_CODEC_IA(dev->base_addr));
-	outb(data, WSS_CODEC_ID(dev->base_addr));
-}
-
-
-/* --------------------------------------------------------------------- */
-
-static unsigned char read_codec(struct device *dev, unsigned char idx)
-{
-	int timeout = 900000;
-
-	/* wait until codec ready */
-	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
-		timeout--;
-	outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr));
-	return inb(WSS_CODEC_ID(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-extern void inline wss_ack_int(struct device *dev)
-{
-	outb(0, WSS_CODEC_STATUS(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_srate_tab[16] = {
-	8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
-	-1, 37800, -1, 44100, 48000, 33075, 9600, 6620
-};
-
-static int wss_srate_index(int srate)
-{
-	int i;
-
-	for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
-		if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
-			return i;
-	return -1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, 
-			     unsigned char fmt, char fdx, char fullcalib)
-{
-	unsigned long time;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	/* Clock and data format register */
-	write_codec(dev, 0x48, fmt);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 0x5c, fmt & 0xf0);
-		/* MCE and interface config reg */	
-		write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
-	} else 
-		/* MCE and interface config reg */
-		write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
-	outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
-	/*
-	 * wait for ACI start
-	 */
-	time = 1000;
-	while (!(read_codec(dev, 0x0b) & 0x20))
-		if (!(--time)) {
-			printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", 
-			       sm_drvname);
-			restore_flags(flags);
-			return -1;
-		}
-	/*
-	 * wait for ACI end
-	 */
-	sti();
-	time = jiffies + HZ/4;
-	while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
-	restore_flags(flags);
-	if ((signed)(jiffies - time) >= 0) {
-		printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", 
-		       sm_drvname);
-		return -1;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, 
-			  unsigned char src_l, unsigned char src_r, 
-			  int igain_l, int igain_r,
-			  int ogain_l, int ogain_r)
-{
-	unsigned char tmp, reg0, reg1, reg6, reg7;
-	static const signed char irqtab[16] = 
-	{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
-		  -1, -1 };
-	static const signed char dmatab[4] = { 1, 2, -1, 3 };
-	
-	tmp = inb(WSS_STATUS(dev->base_addr));
-	if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
-	    (tmp & 0x3f) != 0x0f) {
-		printk(KERN_WARNING "sm: WSS card id register not found, "
-		       "address 0x%lx, ID register 0x%02x\n", 
-		       dev->base_addr, (int)tmp);
-		/* return -1; */
-		SCSTATE->revwss = 0;
-	} else {
-		if ((tmp & 0x80) && ((dev->dma == 0) || 
-				     ((dev->irq >= 8) && (dev->irq != 9)))) {
-			printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
-			       "(except IRQ9) cannot be used on an 8bit "
-			       "card\n", sm_drvname);
-			return -1;
-		}		
-		if (dev->irq > 15 || irqtab[dev->irq] == -1) {
-			printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", 
-			       sm_drvname, (int)dev->irq);
-			return -1;
-		}
-		if (dev->dma > 3 || dmatab[dev->dma] == -1) {
-			printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", 
-			       sm_drvname, (int)dev->dma);
-			return -1;
-		}
-		tmp = irqtab[dev->irq] | dmatab[dev->dma];
-		/* irq probe */
-		outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
-		if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
-			outb(0, WSS_CONFIG(dev->base_addr));
-			printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", 
-			       sm_drvname, dev->irq);
-		}
-		outb(tmp, WSS_CONFIG(dev->base_addr));
-		SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
-	}
-	/*
-	 * initialize the codec
-	 */
-	if (igain_l < 0)
-		igain_l = 0;
-	if (igain_r < 0)
-		igain_r = 0;
-	if (ogain_l > 0)
-		ogain_l = 0;
-	if (ogain_r > 0)
-		ogain_r = 0;
-	reg0 = (src_l << 6) & 0xc0;
-	reg1 = (src_r << 6) & 0xc0;
-	if (reg0 == 0x80 && igain_l >= 20) {
-		reg0 |= 0x20;
-		igain_l -= 20;
-	}
-	if (reg1 == 0x80 && igain_r >= 20) {
-		reg1 |= 0x20;
-		igain_r -= 20;
-	}
-	if (igain_l > 23)
-		igain_l = 23;
-	if (igain_r > 23)
-		igain_r = 23;
-	reg0 |= igain_l * 2 / 3;
-	reg1 |= igain_r * 2 / 3;
-	reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
-	reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
-	write_codec(dev, 9, 0);
-	write_codec(dev, 0, 0x45);
-	if (read_codec(dev, 0) != 0x45)
-		goto codec_err;
-	write_codec(dev, 0, 0xaa);
-	if (read_codec(dev, 0) != 0xaa)
-		goto codec_err;
-	write_codec(dev, 12, 0x40); /* enable MODE2 */
-	write_codec(dev, 16, 0);
-	write_codec(dev, 0, 0x45);
-	SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
-	write_codec(dev, 0, 0xaa);
-	SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
-	if (SCSTATE->crystal) {
-		SCSTATE->revcid = read_codec(dev, 0x19);
-		SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
-		SCSTATE->revcid &= 7;
-		write_codec(dev, 0x10, 0x80); /* maximum output level */
-		write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
-		write_codec(dev, 0x12, 0x80); /* left line input control */
-		write_codec(dev, 0x13, 0x80); /* right line input control */
-		write_codec(dev, 0x16, 0); /* disable alternative freq sel */
-		write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
-		write_codec(dev, 0x1b, 0x00); /* left out no att */
-		write_codec(dev, 0x1d, 0x00); /* right out no att */
-	}
-
-	if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], fdx, 1))
-		goto codec_err;
-
-        write_codec(dev, 0, reg0); /* left input control */
-        write_codec(dev, 1, reg1); /* right input control */
-        write_codec(dev, 2, 0x80); /* left aux#1 input control */
-        write_codec(dev, 3, 0x80); /* right aux#1 input control */
-        write_codec(dev, 4, 0x80); /* left aux#2 input control */
-        write_codec(dev, 5, 0x80); /* right aux#2 input control */
-        write_codec(dev, 6, reg6); /* left dac control */
-        write_codec(dev, 7, reg7); /* right dac control */
-        write_codec(dev, 0xa, 0x2); /* pin control register */
-        write_codec(dev, 0xd, 0x0); /* digital mix control */
-	SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
-	/*
-	 * print revisions
-	 */
-	if (SCSTATE->crystal) 
-		printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
-		       " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
-		       (int)SCSTATE->revv, (int)SCSTATE->revcid);
-	else
-		printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", 
-		       sm_drvname, (int)SCSTATE->revwss, 
-		       (int)SCSTATE->revid);
-	return 0;
- codec_err:
-	outb(0, WSS_CONFIG(dev->base_addr));
-	printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", 
-	       sm_drvname, dev->base_addr);
-	return -1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send)
-{
-        unsigned long flags;
-        static const unsigned char codecmode[2] = { 0x0e, 0x0d };
-        static const unsigned char dmamode[2] = { 
-		DMA_MODE_READ | DMA_MODE_AUTOINIT, 
-		DMA_MODE_WRITE  | DMA_MODE_AUTOINIT
-	};
-	unsigned char oldcodecmode;
-	long abrt;
-	unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf);
-
-        if ((dmabufaddr & 0xffffu) + SCSTATE->dmabuflen > 0x10000)
-                panic("%s: DMA buffer violates DMA boundary!", sm_drvname);
-	send = !!send;
-        save_flags(flags);
-        cli();
-	/*
-	 * perform the final DMA sequence to disable the codec request
-	 */
-	oldcodecmode = read_codec(dev, 9);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	wss_ack_int(dev);
-	if (read_codec(dev, 11) & 0x10) {
-		disable_dma(dev->dma);
-		clear_dma_ff(dev->dma);
-		set_dma_mode(dev->dma, dmamode[oldcodecmode & 1]);
-		set_dma_addr(dev->dma, dmabufaddr);
-		set_dma_count(dev->dma, SCSTATE->dmabuflen);
-		enable_dma(dev->dma);
-		abrt = 0;
-		while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
-	}
-        disable_dma(dev->dma);
-	if (read_codec(dev, 0x8) != SCSTATE->fmt[send])
-		wss_set_codec_fmt(dev, sm, SCSTATE->fmt[send], 0, 0);
-        clear_dma_ff(dev->dma);
-        set_dma_mode(dev->dma, dmamode[send]);
-        set_dma_addr(dev->dma, dmabufaddr);
-        set_dma_count(dev->dma, SCSTATE->dmabuflen);
-        enable_dma(dev->dma);
-	write_codec(dev, 15, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
-	write_codec(dev, 14, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
-		write_codec(dev, 30, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
-	}
-	write_codec(dev, 9, codecmode[send]);
-        restore_flags(flags);
-	printk("sm: wss: %cx fmt: 0x%02x ifc: 0x%02x\n", "rt"[send],
-	       read_codec(dev, 0x8), read_codec(dev, 0x9));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	unsigned char new_ptt;
-	unsigned char *buf;
-	int dmares;
-
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
-	    sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	new_ptt = hdlcdrv_ptt(&sm->hdrv);
-	cli();
-	wss_ack_int(dev);
-	disable_dma(dev->dma);
-	clear_dma_ff(dev->dma);
-	dmares = get_dma_residue(dev->dma);
-	if (dmares <= 0)
-		dmares = SCSTATE->dmabuflen;
-	buf = SCSTATE->dmabuf;
-	if (dmares > SCSTATE->dmabuflen/2) {
-		buf += SCSTATE->dmabuflen/2;
-		dmares -= SCSTATE->dmabuflen/2;
-	}
-#ifdef SM_DEBUG
-	if (!sm->debug_vals.dma_residue || 
-	    dmares < sm->debug_vals.dma_residue)
-		sm->debug_vals.dma_residue = dmares;
-#endif /* SM_DEBUG */
-	dmares--;
-	write_codec(dev, 15, dmares & 0xff);
-	write_codec(dev, 14, dmares >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, dmares & 0xff);
-		write_codec(dev, 30, dmares >> 8);
-	}
-	enable_dma(dev->dma);
-	sm_int_freq(sm);
-	sti();
-	if (new_ptt && !SCSTATE->ptt) {
-		/* starting to transmit */
-		disable_dma(dev->dma);
-		sti();
- 		SCSTATE->dmabufidx = 0;
-		time_exec(sm->debug_vals.demod_cyc, 
-			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf, 
-						 SCSTATE->dmabuflen/2));
- 		setup_dma_wss(dev, sm, 1);
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf + 
-						 SCSTATE->dmabuflen/2,
-						 SCSTATE->dmabuflen/2));
-	} else if (SCSTATE->ptt == 1 && !new_ptt) {
-		/* stopping transmission */
-		disable_dma(dev->dma);
-		sti();
- 		SCSTATE->dmabufidx = 0;
-		setup_dma_wss(dev, sm, 0);
-		SCSTATE->ptt = 0;
-	} else if (SCSTATE->ptt) {
-                SCSTATE->ptt--;
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator(sm, buf, SCSTATE->dmabuflen/2));
-        } else {
-		time_exec(sm->debug_vals.demod_cyc, 
-			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
-		hdlcdrv_arbitrate(dev, &sm->hdrv);
-        }
-        if (new_ptt)
-                SCSTATE->ptt = 2;
-	output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_open(struct device *dev, struct sm_state *sm) 
-{
-	if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
-		printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", 
-		       sizeof(struct sc_state_wss), sizeof(sm->m));
-		return -ENODEV;
-	}
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, WSS_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45))
-		return -ENODEV;
-	/*
-	 * initialize some variables
-	 */
-	if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	SCSTATE->dmabufidx = SCSTATE->ptt = 0;
-
-	memset(&sm->m, 0, sizeof(sm->m));
-	memset(&sm->d, 0, sizeof(sm->d));
-	if (sm->mode_tx->init)
-		sm->mode_tx->init(sm);
-	if (sm->mode_rx->init)
-		sm->mode_rx->init(sm);
-
-	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, 
-			sm->hwdrv->hw_name, dev)) {
-		free_dma(dev->dma);
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
-	setup_dma_wss(dev, sm, 0);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_close(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	release_region(dev->base_addr, WSS_EXTENT);
-	kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, '.');
-	const struct modem_tx_info *mtp = modem_tx_base;
-	const struct modem_rx_info *mrp;
-	int i, j, dv;
-
-	if (!strcmp(mode, "off")) {
-		sm->mode_tx = NULL;
-		sm->mode_rx = NULL;
-		return 0;
-	}
-	if (cp)
-		*cp++ = '\0';
-	else
-		cp = mode;
-	for (; mtp; mtp = mtp->next) {
-		if (mtp->loc_storage > sizeof(sm->m)) {
-			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
-			       sm_drvname, mtp->name, mtp->loc_storage);
-			continue;
-		}
-		if (!mtp->name || strcmp(mtp->name, mode))
-			continue;
-		if ((i = wss_srate_index(mtp->srate)) < 0) 
-			continue;
-		for (mrp = modem_rx_base; mrp; mrp = mrp->next) {
-			if (mrp->loc_storage > sizeof(sm->d)) {
-				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
-				       sm_drvname, mrp->name, mrp->loc_storage);
-				continue;
-			}
-			if (mrp->name && !strcmp(mrp->name, cp) &&
-			    ((j = wss_srate_index(mrp->srate)) >= 0)) {
-				sm->mode_tx = mtp;
-				sm->mode_rx = mrp;
-				SCSTATE->fmt[0] = j;
-				SCSTATE->fmt[1] = i;
-				dv = lcm(sm->mode_tx->dmabuflenmodulo, 
-					 sm->mode_rx->dmabuflenmodulo);
-				SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1;
-				SCSTATE->dmabuflen /= dv;
-				SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */
-	    printk(KERN_DEBUG "sm wss: modtx %u modrx %u srt %u buflen %u\n",
-		   sm->mode_tx->dmabuflenmodulo, sm->mode_rx->dmabuflenmodulo,
-		   sm->mode_rx->srate, SCSTATE->dmabuflen);
-				return 0;
-			}
-		}
-	}
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
-		     struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct sm_ioctl bi;
-	int i;
-	
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-
-	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
-			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
-			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-
-	switch (bi.cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-	case SMCTL_GETMIXER:
-		i = 0;
-		bi.data.mix.sample_rate = sm->mode_rx->srate;
-		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
-		bi.data.mix.mixer_type = SCSTATE->crystal ? 
-			SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
-		if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) 
-		     >> bi.data.mix.reg) & 1) {
-			bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
-			i = 1;
-		}
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return i;
-
-	case SMCTL_SETMIXER:
-		if (!suser())
-			return -EACCES;
-		if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || 
-		     !SCSTATE->crystal) &&
-		    (bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
-		     bi.data.mix.reg >= 0x10))
-			return -EINVAL;
-		if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
-			return -EACCES;
-		write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
-		return 0;
-		
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-struct hardware_info hw_wss = {
-	NEXT_HW_INFO, "wss", sizeof(struct sc_state_wss), 
-	wss_open, wss_close, wss_ioctl, wss_sethw
-};
-#undef NEXT_HW_INFO
-#define NEXT_HW_INFO (&hw_wss)
-
-/* --------------------------------------------------------------------- */
-
-static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm)
-{
-        unsigned long flags;
-	unsigned char oldcodecmode, codecdma;
-	long abrt;
-	unsigned long dmabufaddr1 = virt_to_bus(SCSTATE->dmabuf);
-	unsigned long dmabufaddr2 = virt_to_bus(SCSTATE->dmabuf2);
-
-        if (((dmabufaddr1 & 0xffffu) + SCSTATE->dmabuflen > 0x10000) ||
-	    ((dmabufaddr2 & 0xffffu) + SCSTATE->dmabuflen > 0x10000))
-                panic("%s: DMA buffer violates DMA boundary!", sm_drvname);
-        save_flags(flags);
-        cli();
-	/*
-	 * perform the final DMA sequence to disable the codec request
-	 */
-	oldcodecmode = read_codec(dev, 9);
-        write_codec(dev, 9, 0); /* disable codec DMA */
-	wss_ack_int(dev);
-	if ((codecdma = read_codec(dev, 11)) & 0x10) {
-		disable_dma(dev->dma);
-		disable_dma(sm->hdrv.ptt_out.dma2);
-		clear_dma_ff(dev->dma);
-		set_dma_mode(dev->dma, DMA_MODE_WRITE  | DMA_MODE_AUTOINIT);
-		set_dma_addr(dev->dma, dmabufaddr1);
-		set_dma_count(dev->dma, SCSTATE->dmabuflen);
-		clear_dma_ff(sm->hdrv.ptt_out.dma2);
-		set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_READ  | DMA_MODE_AUTOINIT);
-		set_dma_addr(sm->hdrv.ptt_out.dma2, dmabufaddr2);
-		set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen);
-		enable_dma(dev->dma);
-		enable_dma(sm->hdrv.ptt_out.dma2);
-		abrt = 0;
-		while (((codecdma = read_codec(dev, 11)) & 0x10) ||
-		       ((++abrt) >= 0x10000));
-	}
-        disable_dma(dev->dma);
-        disable_dma(sm->hdrv.ptt_out.dma2);
-	clear_dma_ff(dev->dma);
-	set_dma_mode(dev->dma, DMA_MODE_WRITE  | DMA_MODE_AUTOINIT);
-	set_dma_addr(dev->dma, dmabufaddr1);
-	set_dma_count(dev->dma, SCSTATE->dmabuflen);
-	clear_dma_ff(sm->hdrv.ptt_out.dma2);
-	set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_READ  | DMA_MODE_AUTOINIT);
-	set_dma_addr(sm->hdrv.ptt_out.dma2, dmabufaddr2);
-	set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen);
-        enable_dma(dev->dma);
-        enable_dma(sm->hdrv.ptt_out.dma2);
-	write_codec(dev, 15, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
-	write_codec(dev, 14, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
-		write_codec(dev, 30, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
-	}
-	write_codec(dev, 9, 3);
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	unsigned char *buf1;
-	unsigned char *buf2;
-	unsigned long flags;
-	int dmares1, dmares2;
-
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
-	    sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	save_flags(flags);
-	cli();
-	if (SCSTATE->crystal && (!(read_codec(dev, 0x18) & 0x20))) {
-		/* only regard Crystal Playback interrupts! */
-		wss_ack_int(dev);
-		return;
-	}
-	wss_ack_int(dev);
-	disable_dma(dev->dma);
-	disable_dma(sm->hdrv.ptt_out.dma2);
-	clear_dma_ff(dev->dma);
-	dmares1 = get_dma_residue(dev->dma);
-	clear_dma_ff(sm->hdrv.ptt_out.dma2);
-	dmares2 = get_dma_residue(sm->hdrv.ptt_out.dma2);
-	if (dmares1 <= 0)
-		dmares1 = SCSTATE->dmabuflen;
-	buf1 = SCSTATE->dmabuf;
-	if (dmares1 > SCSTATE->dmabuflen/2) {
-		buf1 += SCSTATE->dmabuflen/2;
-		dmares1 -= SCSTATE->dmabuflen/2;
-	}
-	if (dmares2 <= 0)
-		dmares2 = SCSTATE->dmabuflen;
-	buf2 = SCSTATE->dmabuf2;
-	if (dmares2 > SCSTATE->dmabuflen/2) {
-		buf2 += SCSTATE->dmabuflen/2;
-		dmares2 -= SCSTATE->dmabuflen/2;
-	}
-#ifdef SM_DEBUG
-	if (!sm->debug_vals.dma_residue || 
-	    dmares1 < sm->debug_vals.dma_residue)
-		sm->debug_vals.dma_residue = dmares1;
-#endif /* SM_DEBUG */
-	dmares1--;
-	dmares2--;
-	write_codec(dev, 15, dmares1 & 0xff);
-	write_codec(dev, 14, dmares1 >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, dmares2 & 0xff);
-		write_codec(dev, 30, dmares2 >> 8);
-	}
-	enable_dma(dev->dma);
-	enable_dma(sm->hdrv.ptt_out.dma2);
-	restore_flags(flags);
-	sm_int_freq(sm);
-	sti();
-	if ((SCSTATE->ptt = hdlcdrv_ptt(&sm->hdrv))) 
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator(sm, buf1, SCSTATE->dmabuflen/2));
-	else
-		time_exec(sm->debug_vals.mod_cyc, 
-			  memset(buf1, 0x80, SCSTATE->dmabuflen/2));
-	time_exec(sm->debug_vals.demod_cyc, 
-		  sm->mode_rx->demodulator(sm, buf2, SCSTATE->dmabuflen/2));
-	hdlcdrv_arbitrate(dev, &sm->hdrv);
-	output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_open(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, WSS_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45))
-		return -ENODEV;
-	/*
-	 * initialize some variables
-	 */
-	if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	if (!(SCSTATE->dmabuf2 = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) {
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		return -ENOMEM;
-	}
-	SCSTATE->dmabufidx = SCSTATE->ptt = 0;
-
-	memset(&sm->m, 0, sizeof(sm->m));
-	memset(&sm->d, 0, sizeof(sm->d));
-	if (sm->mode_tx->init)
-		sm->mode_tx->init(sm);
-	if (sm->mode_rx->init)
-		sm->mode_rx->init(sm);
-
-	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
-		return -EBUSY;
-	}
-	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
-		free_dma(dev->dma);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, 
-			sm->hwdrv->hw_name, dev)) {
-		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
-		free_dma(dev->dma);
-		free_dma(sm->hdrv.ptt_out.dma2);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
-	setup_fdx_dma_wss(dev, sm);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_close(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	free_dma(sm->hdrv.ptt_out.dma2);	
-	release_region(dev->base_addr, WSS_EXTENT);
-	kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
-	kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, '.');
-	const struct modem_tx_info *mtp = modem_tx_base;
-	const struct modem_rx_info *mrp;
-	int i, dv;
-
-	if (!strcmp(mode, "off")) {
-		sm->mode_tx = NULL;
-		sm->mode_rx = NULL;
-		return 0;
-	}
-	if (cp)
-		*cp++ = '\0';
-	else
-		cp = mode;
-	for (; mtp; mtp = mtp->next) {
-		if (mtp->loc_storage > sizeof(sm->m)) {
-			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
-			       sm_drvname, mtp->name, mtp->loc_storage);
-			continue;
-		}
-		if (!mtp->name || strcmp(mtp->name, mode))
-			continue;
-		if ((i = wss_srate_index(mtp->srate)) < 0) 
-			continue;
-		for (mrp = modem_rx_base; mrp; mrp = mrp->next) {
-			if (mrp->loc_storage > sizeof(sm->d)) {
-				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
-				       sm_drvname, mrp->name, mrp->loc_storage);
-				continue;
-			}
-			if (mrp->name && !strcmp(mrp->name, cp) &&
-			    mtp->srate == mrp->srate) {
-				sm->mode_tx = mtp;
-				sm->mode_rx = mrp;
-				SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
-				dv = lcm(sm->mode_tx->dmabuflenmodulo, 
-					 sm->mode_rx->dmabuflenmodulo);
-				SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1;
-				SCSTATE->dmabuflen /= dv;
-				SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */
-	    printk(KERN_DEBUG "sm wssfdx: modtx %u modrx %u srt %u buflen %u\n",
-		   sm->mode_tx->dmabuflenmodulo, sm->mode_rx->dmabuflenmodulo,
-		   sm->mode_rx->srate, SCSTATE->dmabuflen);
-				return 0;
-			}
-		}
-	}
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
-			struct hdlcdrv_ioctl *hi, int cmd)
-{
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-
-	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
-			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
-			HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
-			HDLCDRV_PARMASK_MIDIIOBASE;
-
-	return wss_ioctl(dev, sm, ifr, hi, cmd);
-}
-
-/* --------------------------------------------------------------------- */
-
-struct hardware_info hw_wssfdx = {
-	NEXT_HW_INFO, "wssfdx", sizeof(struct sc_state_wss), 
-	wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
-};
-#undef NEXT_HW_INFO
-#define NEXT_HW_INFO (&hw_wssfdx)
-
-/* --------------------------------------------------------------------- */
-
-#undef SCSTATE
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/Makefile linux/drivers/net/soundmodem/Makefile
--- v2.1.26/linux/drivers/net/soundmodem/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/Makefile	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,50 @@
+#
+# Makefile for the soundmodem device driver.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET	:= soundmodem.o
+
+O_OBJS		:= sm.o
+ifeq ($(CONFIG_SOUNDMODEM_SBC),y)
+O_OBJS		+= sm_sbc.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_WSS),y)
+O_OBJS		+= sm_wss.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y)
+O_OBJS		+= sm_afsk1200.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y)
+O_OBJS		+= sm_afsk2666.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_HAPN4800),y)
+O_OBJS		+= sm_hapn4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_PSK4800),y)
+O_OBJS		+= sm_psk4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_FSK9600),y)
+O_OBJS		+= sm_fsk9600.o
+endif
+
+M_OBJS		:= $(O_TARGET)
+
+gentbl:		gentbl.c
+		$(HOSTCC) -Wall $< -o $@ -lm
+
+TBLHDR		:= sm_tbl_afsk1200.h sm_tbl_afsk2666.h sm_tbl_psk4800.h
+TBLHDR		+= sm_tbl_hapn4800.h sm_tbl_fsk9600.h
+
+$(TBLHDR):	gentbl
+		./gentbl
+
+fastdep:	$(TBLHDR)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/gentbl.c linux/drivers/net/soundmodem/gentbl.c
--- v2.1.26/linux/drivers/net/soundmodem/gentbl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/gentbl.c	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,629 @@
+/*****************************************************************************/
+
+/*
+ *      gentbl.c  -- soundcard radio modem driver table generator.
+ *
+ *      Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+/* -------------------------------------------------------------------- */
+
+#define OFFSCOSTABBITS 6
+#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)
+
+static void gentbl_offscostab(FILE *f)
+{
+	int i;
+
+	fprintf(f, "\n/*\n * small cosine table in U8 format\n */\n"
+		"#define OFFSCOSTABBITS %u\n"
+		"#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)\n\n", 
+		OFFSCOSTABBITS);
+	fprintf(f, "static unsigned char offscostab[OFFSCOSTABSIZE] = {\n\t");
+	for (i = 0; i < OFFSCOSTABSIZE; i++) {
+		fprintf(f, "%4u", (int)
+			(128+127.0*cos(i*2.0*M_PI/OFFSCOSTABSIZE)));
+		if (i < OFFSCOSTABSIZE-1) 
+			fprintf(f, "%s", (i & 7) == 7 ? ",\n\t" : ",");
+	}
+	fprintf(f, "\n};\n\n"
+		"#define OFFSCOS(x) offscostab[((x)>>%d)&0x%x]\n\n",
+		16-OFFSCOSTABBITS, OFFSCOSTABSIZE-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK12_SAMPLE_RATE 9600
+#define AFSK12_TX_FREQ_LO 1200
+#define AFSK12_TX_FREQ_HI 2200
+#define AFSK12_CORRLEN 8
+
+static void gentbl_afsk1200(FILE *f)
+{
+        int i;
+
+#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
+#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
+
+	fprintf(f, "\n/*\n * afsk1200 specific tables\n */\n"
+		"#define AFSK12_SAMPLE_RATE %u\n"
+		"#define AFSK12_TX_FREQ_LO %u\n"
+		"#define AFSK12_TX_FREQ_HI %u\n"
+		"#define AFSK12_CORRLEN %u\n\n",
+		AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO, 
+		AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
+	fprintf(f, "#if defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && "
+		"(defined(CONFIG_M586) || defined(CONFIG_M686))\n\n"
+		"static const float afsk12_tx_lo_i_f[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %7f%c", cos(ARGLO(i)),
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+        fprintf(f, "\n};\n\nstatic const float afsk12_tx_lo_q_f[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %7f%c", sin(ARGLO(i)),
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+        fprintf(f, "\n};\n\nstatic const float afsk12_tx_hi_i_f[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %7f%c", cos(ARGHI(i)),
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+        fprintf(f, "\n};\n\nstatic const float afsk12_tx_hi_q_f[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %7f%c", sin(ARGHI(i)), 
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	fprintf(f, "\n};\n\n#else /* CONFIG_SOUNDMODEM_AFSK1200_FP */\n\n"
+		"static const signed char afsk12_tx_lo_i[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %4i%c", (int)(127.0*cos(ARGLO(i))),
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+        fprintf(f, "\n};\n\nstatic const signed char afsk12_tx_lo_q[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %4i%c", (int)(127.0*sin(ARGLO(i))),
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+        fprintf(f, "\n};\n\nstatic const signed char afsk12_tx_hi_i[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %4i%c", (int)(127.0*cos(ARGHI(i))),
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+        fprintf(f, "\n};\n\nstatic const signed char afsk12_tx_hi_q[] = {\n\t");
+        for(i = 0; i < AFSK12_CORRLEN; i++) 
+                fprintf(f, " %4i%c", (int)(127.0*sin(ARGHI(i))), 
+                        (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	fprintf(f, "\n};\n\n#endif /* CONFIG_SOUNDMODEM_AFSK1200_FP */\n\n");
+#undef ARGLO
+#undef ARGHI
+}
+
+/* -------------------------------------------------------------------- */
+
+static const float fsk96_tx_coeff_4[32] = {
+              -0.001152,        0.000554,        0.002698,        0.002753,
+              -0.002033,       -0.008861,       -0.008002,        0.006607,
+               0.023727,        0.018905,       -0.018056,       -0.057957,
+              -0.044368,        0.055683,        0.207667,        0.322048,
+               0.322048,        0.207667,        0.055683,       -0.044368,
+              -0.057957,       -0.018056,        0.018905,        0.023727,
+               0.006607,       -0.008002,       -0.008861,       -0.002033,
+               0.002753,        0.002698,        0.000554,       -0.001152
+};
+
+static const float fsk96_tx_coeff_5[40] = {
+              -0.001009,       -0.000048,        0.001376,        0.002547,
+               0.002061,       -0.001103,       -0.005795,       -0.008170,
+              -0.004017,        0.006924,        0.018225,        0.019238,
+               0.002925,       -0.025777,       -0.048064,       -0.039683,
+               0.013760,        0.104144,        0.200355,        0.262346,
+               0.262346,        0.200355,        0.104144,        0.013760,
+              -0.039683,       -0.048064,       -0.025777,        0.002925,
+               0.019238,        0.018225,        0.006924,       -0.004017,
+              -0.008170,       -0.005795,       -0.001103,        0.002061,
+               0.002547,        0.001376,       -0.000048,       -0.001009
+};
+
+#define HAMMING(x) (0.54-0.46*cos(2*M_PI*(x)));
+
+static inline float hamming(float x)
+{
+	return 0.54-0.46*cos(2*M_PI*x);
+}
+
+static inline float sinc(float x)
+{
+	if (x == 0)
+		return 1;
+	x *= M_PI;
+	return sin(x)/x;
+}
+
+static void gentbl_fsk9600(FILE *f)
+{
+        int i, j, k, l, m;
+	float s;
+	float c[44];
+	float min, max;
+
+	fprintf(f, "\n/*\n * fsk9600 specific tables\n */\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+#if 0
+	memcpy(c+2, fsk96_tx_coeff_4, sizeof(fsk96_tx_coeff_4));
+#else
+	for (i = 0; i < 29; i++)
+		c[3+i] = sinc(1.2*((i-14.0)/4.0))*hamming(i/28.0)/3.5;
+#endif
+	fprintf(f, "static unsigned char fsk96_txfilt_4[] = {\n\t");
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 256; j++) {
+			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+				if (j & k) {
+					for (m = 0; m < 4; m++, l++)
+						s += c[l];
+				} else {
+					for (m = 0; m < 4; m++, l++)
+						s -= c[l];
+				}
+			}
+			s *= 0.75;
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 3 || j < 255)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+#if 0
+	memcpy(c+2, fsk96_tx_coeff_5, sizeof(fsk96_tx_coeff_5));
+#else
+	for (i = 0; i < 36; i++)
+		c[4+i] = sinc(1.2*((i-17.5)/5.0))*hamming(i/35.0)/4.5;
+#endif
+	fprintf(f, "static unsigned char fsk96_txfilt_5[] = {\n\t");
+	for (i = 0; i < 5; i++) {
+		for (j = 0; j < 256; j++) {
+			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+				if (j & k) {
+					for (m = 0; m < 5; m++, l++)
+						s += c[l];
+				} else {
+					for (m = 0; m < 5; m++, l++)
+						s -= c[l];
+				}
+			}
+			s *= 0.75;
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 4 || j < 255)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+}
+	
+/* -------------------------------------------------------------------- */
+
+#define AFSK26_SAMPLERATE  16000
+
+#define AFSK26_NUMCAR      2
+#define AFSK26_FIRSTCAR    2000
+#define AFSK26_MSK_LEN     6
+#define AFSK26_RXOVER      2
+
+#define AFSK26_DEMCORRLEN (2*AFSK26_MSK_LEN)
+
+#define AFSK26_WINDOW(x) ((1-cos(2.0*M_PI*(x)))/2.0)
+
+#define AFSK26_AMPL(x) (((x)?1.0:0.7))
+
+#undef AFSK26_AMPL
+#define AFSK26_AMPL(x) 1
+
+static void gentbl_afsk2666(FILE *f)
+{
+        int i, j, k, l, o;
+        float window[AFSK26_DEMCORRLEN*AFSK26_RXOVER];
+	int cfreq[AFSK26_NUMCAR];
+
+	fprintf(f, "\n/*\n * afsk2666 specific tables\n */\n"
+		"#define AFSK26_DEMCORRLEN %d\n"
+		"#define AFSK26_SAMPLERATE %d\n\n", AFSK26_DEMCORRLEN,
+		AFSK26_SAMPLERATE);
+	fprintf(f, "static const unsigned int afsk26_carfreq[%d] = { ",
+		AFSK26_NUMCAR);
+	for (i = 0; i < AFSK26_NUMCAR; i++) {
+		cfreq[i] = 0x10000*AFSK26_FIRSTCAR/AFSK26_SAMPLERATE+
+			0x10000*i/AFSK26_MSK_LEN/2;
+		fprintf(f, "0x%x", cfreq[i]);
+		if (i < AFSK26_NUMCAR-1)
+			fprintf(f, ", ");
+	}
+	fprintf(f, " };\n\n");
+        for (i = 0; i < AFSK26_DEMCORRLEN*AFSK26_RXOVER; i++)
+                window[i] = AFSK26_WINDOW(((float)i)/(AFSK26_DEMCORRLEN*
+						      AFSK26_RXOVER)) * 127.0;
+        fprintf(f, "\nstatic const struct {\n\t"
+               "signed char i[%d];\n\tsigned char q[%d];\n} afsk26_dem_tables[%d][2] = {\n", 
+               AFSK26_DEMCORRLEN, AFSK26_DEMCORRLEN, AFSK26_RXOVER);
+        for (o = AFSK26_RXOVER-1; o >= 0; o--) {
+                fprintf(f, "\t{\n");
+                for (i = 0; i < AFSK26_NUMCAR; i++) {
+                        j = cfreq[i];
+                        fprintf(f, "\t\t{{ ");
+                        for (l = AFSK26_DEMCORRLEN-1, 
+				     k = (j * o)/AFSK26_RXOVER; l >= 0; 
+                             l--, k = (k+j)&0xffffu)
+                                fprintf(f, "%6d%s", (int)
+					(AFSK26_AMPL(i)*
+					 window[l*AFSK26_RXOVER+o]*
+					 cos(M_PI*k/32768.0)),
+					l ? ", " : " }, { ");
+                        for (l = AFSK26_DEMCORRLEN-1, 
+				     k = (j * o)/AFSK26_RXOVER; l >= 0; 
+                             l--, k = (k+j)&0xffffu)
+                                fprintf(f, "%6d%s", (int)
+					(AFSK26_AMPL(i)*
+					 window[l*AFSK26_RXOVER+o]*
+					 sin(M_PI*k/32768.0)),
+					l ? ", " : " }}");
+                        if (i < 1)
+                                fprintf(f, ",");
+                        fprintf(f, "\n");
+                }
+                fprintf(f, "\t}%s\n", o ? "," : "");
+        }
+        fprintf(f, "};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+#define COSTABBITS 8
+
+static void gentbl_costab(FILE *f)
+{
+        int i;
+
+	fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n"
+		"static const short costab[%d] = {", (1<<COSTABBITS));
+        for (i = 0; i < (1<<COSTABBITS); i++) {
+                if (!(i & 7))
+                        fprintf(f, "\n\t");
+                fprintf(f, "%6d", (int)(32767.0*cos(i*2.0*M_PI/(1<<COSTABBITS))));
+                if (i != ((1<<COSTABBITS)-1))
+                        fprintf(f, ", ");
+        }
+        fprintf(f, "\n};\n\n#define COS(x) costab[((x)>>%d)&0x%x]\n"
+		"#define SIN(x) COS((x)+0xc000)\n\n", 16-COSTABBITS,
+		(1<<COSTABBITS)-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+#define ATAN_TABLEN 1024
+
+static void gentbl_atantab(FILE *f)
+{
+        int i;
+        short x;
+
+	fprintf(f, "\n/*\n"
+		" * arctan table (indexed by i/q; should really be indexed by i/(i+q)\n"
+		" */\n""#define ATAN_TABLEN %d\n\n"
+		"static const unsigned short atan_tab[ATAN_TABLEN+2] = {",
+               ATAN_TABLEN);
+        for (i = 0; i <= ATAN_TABLEN; i++) {
+                if (!(i & 7))
+                        fprintf(f, "\n\t");
+                x = atan(i / (float)ATAN_TABLEN) / M_PI * 0x8000;
+                fprintf(f, "%6d, ", x);
+        }
+        fprintf(f, "%6d\n};\n\n", x);
+	
+}
+
+/* -------------------------------------------------------------------- */
+
+#define PSK48_TXF_OVERSAMPLING 5
+#define PSK48_TXF_NUMSAMPLES 16
+#define PSK48_RXF_LEN     64
+
+static const float psk48_tx_coeff[80] = {
+              -0.000379,       -0.000640,       -0.000000,        0.000772,
+               0.000543,       -0.000629,       -0.001187,       -0.000000,
+               0.001634,        0.001183,       -0.001382,       -0.002603,
+              -0.000000,        0.003481,        0.002472,       -0.002828,
+              -0.005215,       -0.000000,        0.006705,        0.004678,
+              -0.005269,       -0.009584,       -0.000000,        0.012065,
+               0.008360,       -0.009375,       -0.017028,       -0.000000,
+               0.021603,        0.015123,       -0.017229,       -0.032012,
+              -0.000000,        0.043774,        0.032544,       -0.040365,
+              -0.084963,       -0.000000,        0.201161,        0.374060,
+               0.374060,        0.201161,       -0.000000,       -0.084963,
+              -0.040365,        0.032544,        0.043774,       -0.000000,
+              -0.032012,       -0.017229,        0.015123,        0.021603,
+              -0.000000,       -0.017028,       -0.009375,        0.008360,
+               0.012065,       -0.000000,       -0.009584,       -0.005269,
+               0.004678,        0.006705,       -0.000000,       -0.005215,
+              -0.002828,        0.002472,        0.003481,       -0.000000,
+              -0.002603,       -0.001382,        0.001183,        0.001634,
+              -0.000000,       -0.001187,       -0.000629,        0.000543,
+               0.000772,       -0.000000,       -0.000640,       -0.000379
+};
+
+static const float psk48_rx_coeff[PSK48_RXF_LEN] = {
+              -0.000219,        0.000360,        0.000873,        0.001080,
+               0.000747,       -0.000192,       -0.001466,       -0.002436,
+              -0.002328,       -0.000699,        0.002101,        0.004809,
+               0.005696,        0.003492,       -0.001633,       -0.007660,
+              -0.011316,       -0.009627,       -0.001780,        0.009712,
+               0.019426,        0.021199,        0.011342,       -0.008583,
+              -0.030955,       -0.044093,       -0.036634,       -0.002651,
+               0.054742,        0.123101,        0.184198,        0.220219,
+               0.220219,        0.184198,        0.123101,        0.054742,
+              -0.002651,       -0.036634,       -0.044093,       -0.030955,
+              -0.008583,        0.011342,        0.021199,        0.019426,
+               0.009712,       -0.001780,       -0.009627,       -0.011316,
+              -0.007660,       -0.001633,        0.003492,        0.005696,
+               0.004809,        0.002101,       -0.000699,       -0.002328,
+              -0.002436,       -0.001466,       -0.000192,        0.000747,
+               0.001080,        0.000873,        0.000360,       -0.000219
+};
+
+static void gentbl_psk4800(FILE *f)
+{
+        int i, j, k;
+        short x;
+
+	fprintf(f, "\n/*\n * psk4800 specific tables\n */\n"
+		"#define PSK48_TXF_OVERSAMPLING %d\n"
+		"#define PSK48_TXF_NUMSAMPLES %d\n\n"
+		"#define PSK48_SAMPLERATE  8000\n"
+		"#define PSK48_CAR_FREQ    2000\n"
+		"#define PSK48_PSK_LEN     5\n"
+		"#define PSK48_RXF_LEN     %u\n"
+		"#define PSK48_PHASEINC    (0x10000*PSK48_CAR_FREQ/PSK48_SAMPLERATE)\n"
+		"#define PSK48_SPHASEINC   (0x10000/(2*PSK48_PSK_LEN))\n\n"
+		"static const short psk48_tx_table[PSK48_TXF_OVERSAMPLING*"
+		"PSK48_TXF_NUMSAMPLES*8*2] = {", 
+		PSK48_TXF_OVERSAMPLING, PSK48_TXF_NUMSAMPLES, PSK48_RXF_LEN);
+        for (i = 0; i < PSK48_TXF_OVERSAMPLING; i++) {
+                for (j = 0; j < PSK48_TXF_NUMSAMPLES; j++) {
+                        fprintf(f, "\n\t");
+                        for (k = 0; k < 8; k++) {
+                                x = 32767.0 * cos(k*M_PI/4.0) * 
+                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+                                fprintf(f, "%6d, ", x);
+                        }
+                        fprintf(f, "\n\t");
+                        for (k = 0; k < 8; k++) {
+                                x = 32767.0 * sin(k*M_PI/4.0) * 
+                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+                                fprintf(f, "%6d", x);
+                                if (k != 7 || j != PSK48_TXF_NUMSAMPLES-1 || 
+                                    i != PSK48_TXF_OVERSAMPLING-1)
+                                        fprintf(f, ", ");
+                        }
+                }
+        }
+        fprintf(f, "\n};\n\n");
+
+	fprintf(f, "static const short psk48_rx_coeff[PSK48_RXF_LEN] = {\n\t");
+	for (i = 0; i < PSK48_RXF_LEN; i++) {
+		fprintf(f, "%6d", (int)(psk48_rx_coeff[i]*32767.0));
+		if (i < PSK48_RXF_LEN-1)
+			fprintf(f, ",%s", (i & 7) == 7 ? "\n\t" : "");
+	}
+	fprintf(f, "\n};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_hapn4800(FILE *f)
+{
+        int i, j, k, l;
+	float s;
+	float c[40];
+	float min, max;
+
+	fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n");
+	/*
+	 * firstly generate tables for the FM transmitter modulator
+	 */
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 24; i++)
+		c[8+i] = sinc(1.5*((i-11.5)/8.0))*hamming(i/23.0)/2.4;
+	for (i = 0; i < 24; i++)
+		c[i] -= c[i+8];
+	fprintf(f, "static unsigned char hapn48_txfilt_8[] = {\n\t");
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+				if (j & k)
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 7 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 30; i++)
+		c[10+i] = sinc(1.5*((i-14.5)/10.0))*hamming(i/29.0)/2.4;
+	for (i = 0; i < 30; i++)
+		c[i] -= c[i+10];
+	fprintf(f, "static unsigned char hapn48_txfilt_10[] = {\n\t");
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+				if (j & k) 
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 9 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	/*
+	 * secondly generate tables for the PM transmitter modulator
+	 */
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 25; i++)
+		c[i] = sinc(1.4*((i-12.0)/8.0))*hamming(i/24.0)/6.3;
+	for (i = 0; i < 25; i++)
+		for (j = 1; j < 8; j++)
+			c[i] += c[i+j];
+	fprintf(f, "static unsigned char hapn48_txfilt_pm8[] = {\n\t");
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+				if (j & k)
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 7 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 31; i++)
+		c[10+i] = sinc(1.4*((i-15.0)/10.0))*hamming(i/30.0)/7.9;
+	for (i = 0; i < 31; i++)
+		for (j = 1; j < 10; j++)
+			c[i] += c[i+j];
+	fprintf(f, "static unsigned char hapn48_txfilt_pm10[] = {\n\t");
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+				if (j & k) 
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 9 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	
+}
+
+/* -------------------------------------------------------------------- */
+
+static char *progname;
+
+static void gentbl_banner(FILE *f)
+{
+	fprintf(f, "/*\n * THIS FILE IS GENERATED AUTOMATICALLY BY %s, "
+		"DO NOT EDIT!\n */\n\n", progname);
+}
+
+/* -------------------------------------------------------------------- */
+
+void main(int argc, char *argv[])
+{
+	FILE *f;
+	
+	progname = argv[0];
+	if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f);
+	gentbl_afsk1200(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_afsk2666.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f);
+	gentbl_afsk2666(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_psk4800.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_psk4800(f);
+	gentbl_costab(f);
+	gentbl_atantab(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_hapn4800.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_hapn4800(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_fsk9600.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_fsk9600(f);
+	fclose(f);
+	exit(0);
+}
+
+
+/* -------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c
--- v2.1.26/linux/drivers/net/soundmodem/sm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm.c	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,815 @@
+/*****************************************************************************/
+
+/*
+ *	sm.c  -- soundcard radio modem driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     mode string; eg. "wss:afsk1200"
+ *  iobase   base address of the soundcard; common values are 0x220 for sbc,
+ *           0x530 for wss
+ *  irq      interrupt number; common values are 7 or 5 for sbc, 11 for wss
+ *  dma      dma number; common values are 0 or 1
+ *
+ *
+ *  History:
+ *   0.1  21.09.96  Started
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.4  21.01.97  Separately compileable soundcard/modem modules
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include "sm.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static const char sm_drvname[] = "soundmodem";
+static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "soundmodem: version 0.4 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+static const struct modem_tx_info *sm_modem_tx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+	&sm_afsk1200_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+	&sm_afsk2666_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+	&sm_psk4800_tx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+	&sm_hapn4800_8_tx,
+	&sm_hapn4800_10_tx,
+	&sm_hapn4800_pm8_tx,
+	&sm_hapn4800_pm10_tx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+	&sm_fsk9600_4_tx,
+	&sm_fsk9600_5_tx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+	NULL
+};
+
+static const struct modem_rx_info *sm_modem_rx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+	&sm_afsk1200_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+	&sm_afsk2666_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+	&sm_psk4800_rx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+	&sm_hapn4800_8_rx,
+	&sm_hapn4800_10_rx,
+	&sm_hapn4800_pm8_rx,
+	&sm_hapn4800_pm10_rx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+	&sm_fsk9600_4_rx,
+	&sm_fsk9600_5_rx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+	NULL
+};
+
+static const struct hardware_info *sm_hardware_table[] = {
+#ifdef CONFIG_SOUNDMODEM_SBC
+	&sm_hw_sbc,
+#endif /* CONFIG_SOUNDMODEM_SBC */
+#ifdef CONFIG_SOUNDMODEM_WSS
+	&sm_hw_wss,
+	&sm_hw_wssfdx,
+#endif /* CONFIG_SOUNDMODEM_WSS */
+	NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+/* --------------------------------------------------------------------- */
+
+static struct device sm_device[NR_PORTS];
+
+static struct {
+	char *mode;
+	int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase;
+} sm_ports[NR_PORTS] = {
+	{ NULL, -1, 0, 0, 0, -1, -1, -1 },
+};
+
+/* --------------------------------------------------------------------- */
+
+#define UART_RBR(iobase) (iobase+0)
+#define UART_THR(iobase) (iobase+0)
+#define UART_IER(iobase) (iobase+1)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase)    (iobase+0)
+#define LPT_STATUS(iobase)  (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE      0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase)     (iobase)
+#define MIDI_STATUS(iobase)   (iobase+1)
+#define MIDI_READ_FULL 0x80   /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+/* ---------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+#define SP_SER  1
+#define SP_PAR  2
+#define SP_MIDI 4
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+static int check_lpt(unsigned int iobase)
+{
+	unsigned char b1,b2;
+	int i;
+
+	if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+		return 0;
+	if (check_region(iobase, LPT_EXTENT))
+		return 0;
+	b1 = inb(LPT_DATA(iobase));
+	b2 = inb(LPT_CONTROL(iobase));
+	outb(0xaa, LPT_DATA(iobase));
+	i = inb(LPT_DATA(iobase)) == 0xaa;
+	outb(0x55, LPT_DATA(iobase));
+	i &= inb(LPT_DATA(iobase)) == 0x55;
+	outb(0x0a, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+	outb(0x05, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+	outb(b1, LPT_DATA(iobase));
+	outb(b2, LPT_CONTROL(iobase));
+	return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+	{ "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+		return c_uart_unknown;
+	if (check_region(iobase, SER_EXTENT))
+		return c_uart_unknown;
+	b1 = inb(UART_MCR(iobase));
+	outb(b1 | 0x10, UART_MCR(iobase));	/* loopback mode */
+	b2 = inb(UART_MSR(iobase));
+	outb(0x1a, UART_MCR(iobase));
+	b3 = inb(UART_MSR(iobase)) & 0xf0;
+	outb(b1, UART_MCR(iobase));	   /* restore old values */
+	outb(b2, UART_MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(UART_RBR(iobase));
+	inb(UART_RBR(iobase));
+	outb(0x01, UART_FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, UART_SCR(iobase));
+		b1 = inb(UART_SCR(iobase));
+		outb(0xa5, UART_SCR(iobase));
+		b2 = inb(UART_SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int check_midi(unsigned int iobase)
+{
+	unsigned long timeout;
+	unsigned long flags;
+	unsigned char b;
+
+	if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+		return 0;
+	if (check_region(iobase, MIDI_EXTENT))
+		return 0;
+	timeout = jiffies + (HZ / 100);
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	save_flags(flags);
+	cli();
+	outb(0xff, MIDI_DATA(iobase));
+	b = inb(MIDI_STATUS(iobase));
+	restore_flags(flags);
+	if (!(b & MIDI_WRITE_EMPTY))
+		return 0;
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm)
+{
+	int invert_dcd = 0;
+	int invert_ptt = 0;
+
+	int ptt = hdlcdrv_ptt(&sm->hdrv) ^ invert_ptt;
+	int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
+
+	if (sm->hdrv.ptt_out.flags & SP_SER) {
+		outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
+		outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_PAR) {
+		outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
+		outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sm_output_open(struct sm_state *sm)
+{
+	enum uart u = c_uart_unknown;
+
+	sm->hdrv.ptt_out.flags = 0;
+	if (sm->hdrv.ptt_out.seriobase > 0 &&
+	    sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+	    ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
+		sm->hdrv.ptt_out.flags |= SP_SER;
+		request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
+		outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
+		/* 5 bits, 1 stop, no parity, no break, Div latch access */
+		outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
+		outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
+		outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
+		/* LCR and MCR set by output_status */
+	}
+	if (sm->hdrv.ptt_out.pariobase > 0 &&
+	    sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+	    check_lpt(sm->hdrv.ptt_out.pariobase)) {
+		sm->hdrv.ptt_out.flags |= SP_PAR;
+		request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
+	}
+	if (sm->hdrv.ptt_out.midiiobase > 0 &&
+	    sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+	    check_midi(sm->hdrv.ptt_out.midiiobase)) {
+		sm->hdrv.ptt_out.flags |= SP_MIDI;
+		request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
+			       "sm midi ptt");
+	}
+	sm_output_status(sm);
+
+	printk(KERN_INFO "%s: ptt output:", sm_drvname);
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
+		       uart_str[u]);
+	if (sm->hdrv.ptt_out.flags & SP_PAR)
+		printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
+	if (sm->hdrv.ptt_out.flags & SP_MIDI)
+		printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
+	if (!sm->hdrv.ptt_out.flags)
+		printk(" none");
+	printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sm_output_close(struct sm_state *sm)
+{
+	/* release regions used for PTT output */
+	sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
+	sm_output_status(sm);
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_PAR)
+		release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_MIDI)
+		release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
+	sm->hdrv.ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev);
+static int sm_close(struct device *dev);
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+		    struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static const struct hdlcdrv_ops sm_ops = {
+	sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev)
+{
+	struct sm_state *sm;
+	int err;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_open: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+	if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
+		return -ENODEV;
+	sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
+	err = sm->hwdrv->open(dev, sm);
+	if (err)
+		return err;
+	sm_output_open(sm);
+	MOD_INC_USE_COUNT;
+	printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u\n",
+	       sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
+	       sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_close(struct device *dev)
+{
+	struct sm_state *sm;
+	int err = -ENODEV;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_close: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+
+	if (sm->hwdrv && sm->hwdrv->close)
+		err = sm->hwdrv && sm->hwdrv->close(dev, sm);
+	sm_output_close(sm);
+	MOD_DEC_USE_COUNT;
+	printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
+	       sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
+	return err;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, ':');
+	const struct hardware_info **hwp = sm_hardware_table;
+
+	if (!cp)
+		cp = mode;
+	else {
+		*cp++ = '\0';
+		while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode))
+			hwp++;
+		if (!hwp || !*hwp || !(*hwp)->hw_name)
+			return -EINVAL;
+		if ((*hwp)->loc_storage > sizeof(sm->hw)) {
+			printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
+			       sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage);
+			return -EINVAL;
+		}
+		sm->hwdrv = *hwp;
+	}
+	if (!*cp)
+		return 0;
+	if (sm->hwdrv && sm->hwdrv->sethw)
+		return sm->hwdrv->sethw(dev, sm, cp);
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+		    struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_state *sm;
+	struct sm_ioctl bi;
+	unsigned long flags;
+	unsigned int newdiagmode;
+	unsigned int newdiagflags;
+	char *cp;
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp = sm_modem_rx_table;
+	const struct hardware_info **hwp = sm_hardware_table;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE) {
+		if (!sm->hwdrv || !sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+		return -ENOIOCTLCMD;
+	}
+	switch (hi->cmd) {
+	default:
+		if (sm->hwdrv && sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETMODE:
+		cp = hi->data.modename;
+		if (sm->hwdrv && sm->hwdrv->hw_name)
+			cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
+		else
+			cp += sprintf(cp, "<unspec>:");
+		if (sm->mode_tx && sm->mode_tx->name)
+			cp += sprintf(cp, "%s", sm->mode_tx->name);
+		else
+			cp += sprintf(cp, "<unspec>");
+		if (!sm->mode_rx || !sm->mode_rx ||
+		    strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
+			if (sm->mode_rx && sm->mode_rx->name)
+				cp += sprintf(cp, ",%s", sm->mode_rx->name);
+			else
+				cp += sprintf(cp, ",<unspec>");
+		}
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return sethw(dev, sm, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		cp = hi->data.modename;
+		while (*hwp) {
+			if ((*hwp)->hw_name)
+				cp += sprintf("%s:,", (*hwp)->hw_name);
+			hwp++;
+		}
+		while (*mtp) {
+			if ((*mtp)->name)
+				cp += sprintf(">%s,", (*mtp)->name);
+			mtp++;
+		}
+		while (*mrp) {
+			if ((*mrp)->name)
+				cp += sprintf("<%s,", (*mrp)->name);
+			mrp++;
+		}
+		cp[-1] = '\0';
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
+		return 0;
+
+#ifdef SM_DEBUG
+	case SMCTL_GETDEBUG:
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
+		bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
+		bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
+		bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
+		bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
+		sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
+			sm->debug_vals.dma_residue = 0;
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
+#endif /* SM_DEBUG */
+
+	case SMCTL_DIAGNOSE:
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
+		newdiagmode = bi.data.diag.mode;
+		newdiagflags = bi.data.diag.flags;
+		if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
+			return -EINVAL;
+		bi.data.diag.mode = sm->diag.mode;
+		bi.data.diag.flags = sm->diag.flags;
+		bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
+		if (sm->diag.mode != newdiagmode) {
+			save_flags(flags);
+			cli();
+			sm->diag.ptr = -1;
+			sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+			sm->diag.mode = newdiagmode;
+			restore_flags(flags);
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (bi.data.diag.datalen > DIAGDATALEN)
+			bi.data.diag.datalen = DIAGDATALEN;
+		if (sm->diag.ptr < bi.data.diag.datalen) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (copy_to_user(bi.data.diag.data, sm->diag.data,
+				 bi.data.diag.datalen * sizeof(short)))
+			return -EFAULT;
+		bi.data.diag.flags |= SM_DIAGFLAG_VALID;
+		save_flags(flags);
+		cli();
+		sm->diag.ptr = -1;
+		sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+		sm->diag.mode = newdiagmode;
+		restore_flags(flags);
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+static
+#endif /* MODULE */
+int sm_init(void)
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct sm_state *sm;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+	printk(sm_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		sprintf(ifname, "sm%d", i);
+
+		if (!sm_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			sm_ports[i].iobase = sm_ports[i].irq = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state),
+					     ifname, sm_ports[i].iobase,
+					     sm_ports[i].irq, sm_ports[i].dma);
+		if (!j) {
+			sm = (struct sm_state *)dev->priv;
+			sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2;
+			sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
+			sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
+			sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
+			if (set_hw && sethw(dev, sm, sm_ports[i].mode))
+				set_hw = 0;
+			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       sm_drvname);
+		}
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode = NULL;
+static int iobase = -1;
+static int irq = -1;
+static int dma = -1;
+static int dma2 = -1;
+static int serio = 0;
+static int pario = 0;
+static int midiio = 0;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "s");
+MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "soundmodem base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "soundmodem interrupt");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "soundmodem dma channel");
+MODULE_PARM(dma2, "i");
+MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only");
+MODULE_PARM(serio, "i");
+MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port");
+MODULE_PARM(pario, "i");
+MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port");
+MODULE_PARM(midiio, "i");
+MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Soundcard amateur radio modem driver");
+
+#endif
+
+int init_module(void)
+{
+	if (mode) {
+		if (iobase == -1)
+			iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530;
+		if (irq == -1)
+			irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11;
+		if (dma == -1)
+			dma = 1;
+	}
+	sm_ports[0].mode = mode;
+	sm_ports[0].iobase = iobase;
+	sm_ports[0].irq = irq;
+	sm_ports[0].dma = dma;
+	sm_ports[0].dma2 = dma2;
+	sm_ports[0].seriobase = serio;
+	sm_ports[0].pariobase = pario;
+	sm_ports[0].midiiobase = midiio;
+	sm_ports[1].mode = NULL;
+
+	return sm_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	printk(KERN_INFO "sm: cleanup_module called\n");
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		struct sm_state *sm = (struct sm_state *)dev->priv;
+
+		if (sm) {
+			if (sm->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "sm: invalid magic in "
+				       "cleanup_module\n");
+			else
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode
+ * mode: hw:modem
+ * hw: sbc, wss, wssfdx
+ * modem: afsk1200, fsk9600
+ */
+
+void sm_setup(char *str, int *ints)
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 3)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", sm_drvname);
+		return;
+	}
+	sm_ports[i].mode = str;
+	sm_ports[i].iobase = ints[1];
+	sm_ports[i].irq = ints[2];
+	sm_ports[i].dma = ints[3];
+	sm_ports[i].dma2 = (ints[0] >= 4) ? ints[4] : 0;
+	sm_ports[i].seriobase = (ints[0] >= 5) ? ints[5] : 0;
+	sm_ports[i].pariobase = (ints[0] >= 6) ? ints[6] : 0;
+	sm_ports[i].midiiobase = (ints[0] >= 7) ? ints[7] : 0;
+	if (i < NR_PORTS-1)
+		sm_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/sm.h linux/drivers/net/soundmodem/sm.h
--- v2.1.26/linux/drivers/net/soundmodem/sm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm.h	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,360 @@
+/*****************************************************************************/
+
+/*
+ *	sm.h  --  soundcard radio modem driver internal header.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#ifndef _SM_H
+#define _SM_H
+
+/* ---------------------------------------------------------------------- */
+
+#include <linux/config.h>
+#include <linux/hdlcdrv.h>
+#include <linux/soundmodem.h>
+
+#define SM_DEBUG
+
+/* --------------------------------------------------------------------- */
+
+#define DMA_MODE_AUTOINIT      0x10
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct sm_state {
+	struct hdlcdrv_state hdrv;
+
+	const struct modem_tx_info *mode_tx;
+	const struct modem_rx_info *mode_rx;
+
+	const struct hardware_info *hwdrv;
+
+	/*
+	 * Hardware (soundcard) access routines state
+	 */
+	union {
+		long hw[32/sizeof(long)];
+	} hw;
+
+	/*
+	 * state of the modem code
+	 */
+	union {
+		long m[32/sizeof(long)];
+	} m;
+	union {
+		long d[256/sizeof(long)];
+	} d;
+
+#define DIAGDATALEN 64
+	struct diag_data {
+		unsigned int mode;
+		unsigned int flags;
+		volatile int ptr;
+		short data[DIAGDATALEN];
+	} diag;
+
+
+#ifdef SM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		unsigned mod_cyc;
+		unsigned demod_cyc;
+		unsigned dma_residue;
+	} debug_vals;
+#endif /* SM_DEBUG */
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Mode definition structure
+ */
+
+struct modem_tx_info {
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+	unsigned int dmabuflenmodulo;
+	void (*modulator)(struct sm_state *, unsigned char *, int);
+	void (*init)(struct sm_state *);
+};
+
+struct modem_rx_info {
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+	unsigned int dmabuflenmodulo;
+	unsigned int sperbit;
+	void (*demodulator)(struct sm_state *, unsigned char *, int);
+	void (*init)(struct sm_state *);
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Soundcard driver definition structure
+ */
+
+struct hardware_info {
+	char *hw_name; /* used for request_{region,irq,dma} */
+	unsigned int loc_storage;
+	/*
+	 * mode specific open/close
+	 */
+	int (*open)(struct device *, struct sm_state *);
+	int (*close)(struct device *, struct sm_state *);
+	int (*ioctl)(struct device *, struct sm_state *, struct ifreq *,
+		     struct hdlcdrv_ioctl *, int);
+	int (*sethw)(struct device *, struct sm_state *, char *);
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+extern const char sm_drvname[];
+extern const char sm_drvinfo[];
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== diagnostics stuff ===============================
+ */
+
+extern inline void diag_trigger(struct sm_state *sm)
+{
+	if (sm->diag.ptr < 0)
+		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
+			sm->diag.ptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
+#define SHRT_MIN (-SHRT_MAX-1)
+
+extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
+{
+	int val;
+
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void diag_add_one(struct sm_state *sm, int val)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
+	    sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (vali > SHRT_MAX)
+		vali = SHRT_MAX;
+	if (vali < SHRT_MIN)
+		vali = SHRT_MIN;
+	if (valq > SHRT_MAX)
+		valq = SHRT_MAX;
+	if (valq < SHRT_MIN)
+		valq = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = vali;
+	sm->diag.data[sm->diag.ptr++] = valq;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== utility functions ===============================
+ */
+
+extern inline unsigned int hweight32(unsigned int w)
+	__attribute__ ((unused));
+extern inline unsigned int hweight16(unsigned short w)
+	__attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+        __attribute__ ((unused));
+
+extern inline unsigned int hweight32(unsigned int w)
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+extern inline unsigned int hweight16(unsigned short w)
+{
+        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+        res = (res & 0x3333) + ((res >> 2) & 0x3333);
+        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+        res = (res & 0x33) + ((res >> 2) & 0x33);
+        return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+{
+	for (;;) {
+		if (!x)
+			return y;
+		if (!y)
+			return x;
+		if (x > y)
+			x %= y;
+		else
+			y %= x;
+	}
+}
+
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+{
+	return x * y / gcd(x, y);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== profiling =======================================
+ */
+
+
+#if defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686))
+
+/*
+ * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
+ * in fact, overflowing modems would require over 2THz clock speeds :-)
+ */
+
+#define time_exec(var,cmd)                                      \
+({                                                              \
+	unsigned int cnt1, cnt2, cnt3;                          \
+	__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3));  \
+	cmd;                                                    \
+	__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3));  \
+	var = cnt2-cnt1;                                        \
+})
+#else /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
+
+#define time_exec(var,cmd) cmd
+
+#endif /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
+
+/* --------------------------------------------------------------------- */
+
+extern const struct modem_tx_info sm_afsk1200_tx;
+extern const struct modem_tx_info sm_afsk2666_tx;
+extern const struct modem_tx_info sm_psk4800_tx;
+extern const struct modem_tx_info sm_hapn4800_8_tx;
+extern const struct modem_tx_info sm_hapn4800_10_tx;
+extern const struct modem_tx_info sm_hapn4800_pm8_tx;
+extern const struct modem_tx_info sm_hapn4800_pm10_tx;
+extern const struct modem_tx_info sm_fsk9600_4_tx;
+extern const struct modem_tx_info sm_fsk9600_5_tx;
+
+extern const struct modem_rx_info sm_afsk1200_rx;
+extern const struct modem_rx_info sm_afsk2666_rx;
+extern const struct modem_rx_info sm_psk4800_rx;
+extern const struct modem_rx_info sm_hapn4800_8_rx;
+extern const struct modem_rx_info sm_hapn4800_10_rx;
+extern const struct modem_rx_info sm_hapn4800_pm8_rx;
+extern const struct modem_rx_info sm_hapn4800_pm10_rx;
+extern const struct modem_rx_info sm_fsk9600_4_rx;
+extern const struct modem_rx_info sm_fsk9600_5_rx;
+
+extern const struct hardware_info sm_hw_sbc;
+extern const struct hardware_info sm_hw_wss;
+extern const struct hardware_info sm_hw_wssfdx;
+
+extern const struct modem_tx_info *sm_modem_tx_table[];
+extern const struct modem_rx_info *sm_modem_rx_table[];
+extern const struct hardware_info *sm_hardware_table[];
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm);
+/*void sm_output_open(struct sm_state *sm);*/
+/*void sm_output_close(struct sm_state *sm);*/
+
+/* --------------------------------------------------------------------- */
+
+extern void inline sm_int_freq(struct sm_state *sm)
+{
+#ifdef SM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	sm->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
+		sm->debug_vals.last_jiffies = cur_jiffies;
+		sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
+		sm->debug_vals.cur_intcnt = 0;
+	}
+#endif /* SM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* _SM_H */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/sm_afsk1200.c linux/drivers/net/soundmodem/sm_afsk1200.c
--- v2.1.26/linux/drivers/net/soundmodem/sm_afsk1200.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_afsk1200.c	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,273 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk1200.h  -- soundcard radio modem driver, 1200 baud AFSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk1200.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk12 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	unsigned char last_rxbit;
+	union {
+		signed char c[8];
+		float f[8];
+	} filt;
+};
+
+struct mod_state_afsk12 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int bit_pll;
+};
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
+	static const int dds_inc[2] = { AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE,
+					AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE };
+	int j, k;
+
+	for (; buflen >= 8; buflen -= 8) {
+		if (st->shreg <= 1)
+			st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		st->tx_bit = (st->tx_bit ^ 
+				       (!(st->shreg & 1))) & 1;
+		st->shreg >>= 1;
+		k = dds_inc[st->tx_bit & 1];
+		for (j = 0; j < 8; j++) {
+			*buf++ = OFFSCOS(st->bit_pll);
+			st->bit_pll += k;
+		}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+
+/*
+ * should eventually move to an asm header file
+ */
+
+
+
+
+#if defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686))
+
+
+
+#define ENV_STORAGE unsigned char fpu_save[108];
+
+#define ENV_SAVE asm("fsave %0;\n\tfninit;\n\t" : "=m" (*fpu_save) : : "memory");
+#define ENV_RESTORE asm("frstor %0;\n\t" : : "m" (*fpu_save));
+
+static inline float convolution8(const float *st, const float *coeff)
+{
+	float f;
+
+	/*
+	 * from Phil Karn, KA9Q's home page
+	 */
+	asm volatile ("flds (%1);\n\t"
+	    "fmuls (%2);\n\t"
+	    "flds 4(%1);\n\t"
+	    "fmuls 4(%2);\n\t"
+	    "flds 8(%1);\n\t"
+	    "fmuls 8(%2);\n\t"
+	    "fxch %%st(2);\n\t"
+	    "faddp;\n\t"
+	    "flds 12(%1);\n\t"
+	    "fmuls 12(%2);\n\t"
+	    "fxch %%st(2);\n\t"
+	    "faddp;\n\t"
+	    "flds 16(%1);\n\t"
+	    "fmuls 16(%2);\n\t"
+	    "fxch %%st(2);\n\t"
+	    "faddp;\n\t"
+	    "flds 20(%1);\n\t"
+	    "fmuls 20(%2);\n\t"
+	    "fxch %%st(2);\n\t"
+	    "faddp;\n\t"
+	    "flds 24(%1);\n\t"
+	    "fmuls 24(%2);\n\t"
+	    "fxch %%st(2);\n\t"
+	    "faddp;\n\t"
+	    "flds 28(%1);\n\t"
+	    "fmuls 28(%2);\n\t"
+	    "fxch %%st(2);\n\t"
+	    "faddp;\n\t"
+	    "faddp;\n\t"
+	    "fmul %%st(0),%%st;\n\t" :
+	    "=t" (f) :
+	    "r" (st),
+	    "r" (coeff) : "memory");
+
+	return f;
+}
+
+static inline int do_filter_1200(struct demod_state_afsk12 *st, unsigned char newval)
+{
+	float sum;
+
+	memmove(st->filt.f+1, st->filt.f,sizeof(st->filt.f) - sizeof(st->filt.f[0]));
+	st->filt.f[0] = (((int)newval)-0x80);
+
+	sum = convolution8(st->filt.f, afsk12_tx_lo_i);
+	sum += convolution8(st->filt.f, afsk12_tx_lo_q);
+	sum -= convolution8(st->filt.f, afsk12_tx_hi_i);
+	sum -= convolution8(st->filt.f, afsk12_tx_hi_q);
+	return sum;
+}
+
+#else /* defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
+
+#define ENV_STORAGE 
+#define ENV_SAVE
+#define ENV_RESTORE
+
+static inline void datamove8(signed char *st, unsigned char newval)
+{
+	memmove(st+1, st, 7);
+	*st = newval - 0x80;
+}
+
+static inline int convolution8(const signed char *st, const signed char *coeff)
+{
+	int sum = (st[0] * coeff[0]);
+	
+	sum += (st[1] * coeff[1]);
+	sum += (st[2] * coeff[2]);
+	sum += (st[3] * coeff[3]);
+	sum += (st[4] * coeff[4]);
+	sum += (st[5] * coeff[5]);
+	sum += (st[6] * coeff[6]);
+	sum += (st[7] * coeff[7]);
+
+	sum >>= 7;
+	return sum * sum;
+}
+
+static inline int do_filter_1200(struct demod_state_afsk12 *st, unsigned char newval)
+{
+	int sum;
+
+	datamove8(st->filt.c, newval);
+	
+	sum = convolution8(st->filt.c, afsk12_tx_lo_i);
+	sum += convolution8(st->filt.c, afsk12_tx_lo_q);
+	sum -= convolution8(st->filt.c, afsk12_tx_hi_i);
+	sum -= convolution8(st->filt.c, afsk12_tx_hi_q);
+	return sum;
+}
+
+#endif /* defined (CONFIG_SOUNDMODEM__AFSK1200_FP) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
+
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_1200(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	int j;
+	int sum;
+	unsigned char newsample;
+	ENV_STORAGE;
+
+	ENV_SAVE;
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_1200(st, *buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr
+				[st->bit_pll < 0x9000];
+			j = 4 * hweight8(st->dcd_shreg & 0x38)
+				- hweight16(st->dcd_shreg & 0x7c0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+	}
+	ENV_RESTORE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_1200(struct sm_state *sm)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+
+       	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk1200_tx = {
+	"afsk1200", sizeof(struct mod_state_afsk12), 
+	AFSK12_SAMPLE_RATE, 1200, AFSK12_SAMPLE_RATE/1200, modulator_1200, NULL
+};
+
+const struct modem_rx_info sm_afsk1200_rx = {
+	"afsk1200", sizeof(struct demod_state_afsk12), 
+	AFSK12_SAMPLE_RATE, 1200, AFSK12_SAMPLE_RATE/1200, 
+	AFSK12_SAMPLE_RATE/1200, demodulator_1200, demod_init_1200
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/sm_fsk9600.c linux/drivers/net/soundmodem/sm_fsk9600.c
--- v2.1.26/linux/drivers/net/soundmodem/sm_fsk9600.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_fsk9600.c	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,239 @@
+/*****************************************************************************/
+
+/*
+ *	sm_fsk9600.h  --  soundcard radio modem driver, 
+ *                        9600 baud G3RUH compatible FSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_fsk9600.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_fsk96 {
+	unsigned int shreg;
+	unsigned long descram;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+};
+
+struct mod_state_fsk96 {
+	unsigned int shreg;
+	unsigned long scram;
+	unsigned char tx_bit;
+};
+
+/* --------------------------------------------------------------------- */
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 4; buflen -= 4) {
+		if (st->shreg <= 1)
+			st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		st->scram = (st->scram << 1) | (st->scram & 1);
+		st->scram ^= !(st->shreg & 1);
+		st->shreg >>= 1;
+		if (st->scram & (SCRAM_TAP1 << 1))
+			st->scram ^= SCRAM_TAPN << 1;
+		st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+		cp = fsk96_txfilt_4 + (st->tx_bit & 0xff);
+		for (j = 0; j < 4; j++, cp += 0x100)
+			*buf++ = *cp;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x4000;
+		curbit = (*buf >= 0x80);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0xa000];
+			st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - 
+				!!(st->dcd_shreg & 0x10);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 5; buflen -= 5) {
+		if (st->shreg <= 1)
+			st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		st->scram = (st->scram << 1) | (st->scram & 1);
+		st->scram ^= !(st->shreg & 1);
+		st->shreg >>= 1;
+		if (st->scram & (SCRAM_TAP1 << 1))
+			st->scram ^= SCRAM_TAPN << 1;
+		st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+		cp = fsk96_txfilt_5 + (st->tx_bit & 0xff);
+		for (j = 0; j < 5; j++, cp += 0x100)
+			*buf++ = *cp;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x3333;
+		curbit = (*buf >= 0x80);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0x9999];
+			st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - 
+				hweight8(st->dcd_shreg & 0x70);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_9600(struct sm_state *sm)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+
+	st->dcd_time = 240;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_4_tx = {
+	"fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600, 4,
+	modulator_9600_4, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_4_rx = {
+	"fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 4, 4,
+	demodulator_9600_4, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_5_tx = {
+	"fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600, 5, 
+	modulator_9600_5, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_5_rx = {
+	"fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 5, 5, 
+	demodulator_9600_5, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/sm_hapn4800.c linux/drivers/net/soundmodem/sm_hapn4800.c
--- v2.1.26/linux/drivers/net/soundmodem/sm_hapn4800.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_hapn4800.c	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,347 @@
+/*****************************************************************************/
+
+/*
+ *	sm_hapn4800.h  -- soundcard radio modem driver, 4800 baud HAPN modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  This module implements a (hopefully) HAPN (Hamilton Area Packet
+ *  Network) compatible 4800 baud modem.
+ *  The HAPN modem uses kind of "duobinary signalling" (not really,
+ *  duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling
+ *  instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see
+ *  Proakis, Digital Communications).
+ *  The code is untested. It is compatible with itself (i.e. it can decode
+ *  the packets it sent), but I could not test if it is compatible with
+ *  any "real" HAPN modem, since noone uses it in my region of the world.
+ *  Feedback therefore welcome.
+ */
+
+#include "sm.h"
+#include "sm_tbl_hapn4800.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_hapn48 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_bit;
+	unsigned char last_bit2;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	int inphist[5];
+	int lvlhi, lvllo;
+};
+
+struct mod_state_hapn48 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+};
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_10(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 10; buflen -= 10) {
+		if (st->shreg <= 1)
+			st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		st->tx_bit = ((st->tx_bit << 1) |
+				     (st->tx_bit & 1));
+		st->tx_bit ^= (!(st->shreg & 1));
+		st->shreg >>= 1;
+		cp = hapn48_txfilt_10 + (st->tx_bit & 0xf);
+		for (j = 0; j < 10; j++, cp += 0x10)
+			*buf++ = *cp;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_8(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 8; buflen -= 8) {
+		if (st->shreg <= 1)
+			st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+		st->tx_bit ^= !(st->shreg & 1);
+		st->shreg >>= 1;
+		cp = hapn48_txfilt_8 + (st->tx_bit & 0xf);
+		for (j = 0; j < 8; j++, cp += 0x10)
+			*buf++ = *cp;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm10(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 10; buflen -= 10) {
+		if (st->shreg <= 1)
+			st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		st->tx_bit = ((st->tx_bit << 1) |
+				     (st->tx_bit & 1));
+		st->tx_bit ^= (!(st->shreg & 1));
+		st->shreg >>= 1;
+		cp = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
+		for (j = 0; j < 10; j++, cp += 0x10)
+			*buf++ = *cp;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm8(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+	int j;
+	const unsigned char *cp;
+
+	for (; buflen >= 8; buflen -= 8) {
+		if (st->shreg <= 1)
+			st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+		st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+		st->tx_bit ^= !(st->shreg & 1);
+		st->shreg >>= 1;
+		cp = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
+		for (j = 0; j < 8; j++, cp += 0x10)
+			*buf++ = *cp;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_10(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->inphist[4] = st->inphist[3];
+		st->inphist[3] = st->inphist[2];
+		st->inphist[2] = st->inphist[1];
+		st->inphist[1] = st->inphist[0];
+		st->inphist[0] = ((int)(*buf)-0x80) << 8;
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (st->inphist[2] > st->lvlhi)
+			st->lvlhi = st->inphist[2];
+		if (st->inphist[2] < st->lvllo)
+			st->lvllo = st->inphist[2];
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x199a;
+		curst = cursync = 0;
+		if (st->inphist[2] > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (st->inphist[2] > st->inphist[1] &&
+				   st->inphist[2] > st->inphist[3] &&
+				   st->inphist[2] > st->inphist[0] &&
+				   st->inphist[2] > st->inphist[4]);
+		} else if (st->inphist[2] < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (st->inphist[2] < st->inphist[1] &&
+				   st->inphist[2] < st->inphist[3] &&
+				   st->inphist[2] < st->inphist[0] &&
+				   st->inphist[2] < st->inphist[4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - 
+				hweight32(st->dcd_shreg & 0xe739ce70);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, st->inphist[2]);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_8(struct sm_state *sm, unsigned char *buf, int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->inphist[4] = st->inphist[3];
+		st->inphist[3] = st->inphist[2];
+		st->inphist[2] = st->inphist[1];
+		st->inphist[1] = st->inphist[0];
+		st->inphist[0] = ((int)(*buf)-0x80) << 8;
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (st->inphist[2] > st->lvlhi)
+			st->lvlhi = st->inphist[2];
+		if (st->inphist[2] < st->lvllo)
+			st->lvllo = st->inphist[2];
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		curst = cursync = 0;
+		if (st->inphist[2] > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (st->inphist[2] > st->inphist[1] &&
+				   st->inphist[2] > st->inphist[3] &&
+				   st->inphist[2] > st->inphist[0] &&
+				   st->inphist[2] > st->inphist[4]);
+		} else if (st->inphist[2] < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (st->inphist[2] < st->inphist[1] &&
+				   st->inphist[2] < st->inphist[3] &&
+				   st->inphist[2] < st->inphist[0] &&
+				   st->inphist[2] < st->inphist[4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - 
+				hweight32(st->dcd_shreg & 0xbbbbbbbb);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, st->inphist[2]);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_hapn4800(struct sm_state *sm)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+
+	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_8_tx = {
+	"hapn4800", sizeof(struct mod_state_hapn48), 
+	38400, 4800, 8, modulator_hapn4800_8, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_8_rx = {
+	"hapn4800", sizeof(struct demod_state_hapn48), 
+	38400, 4800, 8, 8, demodulator_hapn4800_8, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_10_tx = {
+	"hapn4800", sizeof(struct mod_state_hapn48), 
+	48000, 4800, 10, 
+	modulator_hapn4800_10, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_10_rx = {
+	"hapn4800", sizeof(struct demod_state_hapn48), 
+	48000, 4800, 10, 10, demodulator_hapn4800_10, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm8_tx = {
+	"hapn4800pm", sizeof(struct mod_state_hapn48), 
+	38400, 4800, 8, modulator_hapn4800_pm8, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm8_rx = {
+	"hapn4800pm", sizeof(struct demod_state_hapn48), 
+	38400, 4800, 8, 8, demodulator_hapn4800_8, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm10_tx = {
+	"hapn4800pm", sizeof(struct mod_state_hapn48), 
+	48000, 4800, 10, 
+	modulator_hapn4800_pm10, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm10_rx = {
+	"hapn4800pm", sizeof(struct demod_state_hapn48), 
+	48000, 4800, 10, 10, demodulator_hapn4800_10, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c
--- v2.1.26/linux/drivers/net/soundmodem/sm_sbc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_sbc.c	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,525 @@
+/*****************************************************************************/
+
+/*
+ *	sm_sbc.h  -- soundcard radio modem driver soundblaster hardware driver
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include "sm.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_sbc {
+	unsigned char revhi, revlo;
+	unsigned char fmt[2];
+	unsigned int dmabuflen;
+	unsigned char *dmabuf;
+	unsigned char dmabufidx;
+	unsigned char ptt;
+};
+
+#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+/* 
+ * the sbc converter's registers 
+ */
+#define DSP_RESET(iobase)        (iobase+0x6)
+#define DSP_READ_DATA(iobase)    (iobase+0xa)
+#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
+#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
+#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
+#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
+#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
+#define SBC_EXTENT               16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+#define SBC_OUTPUT             0x14
+#define SBC_INPUT              0x24
+#define SBC_BLOCKSIZE          0x48
+#define SBC_HI_OUTPUT          0x91 
+#define SBC_HI_INPUT           0x99 
+#define SBC_LO_OUTPUT_AUTOINIT 0x1c
+#define SBC_LO_INPUT_AUTOINIT  0x2c
+#define SBC_HI_OUTPUT_AUTOINIT 0x90 
+#define SBC_HI_INPUT_AUTOINIT  0x98
+#define SBC_IMMED_INT          0xf2
+#define SBC_GET_REVISION       0xe1
+#define ESS_GET_REVISION       0xe7
+#define SBC_SPEAKER_ON         0xd1
+#define SBC_SPEAKER_OFF        0xd3
+#define SBC_DMA_ON             0xd0
+#define SBC_DMA_OFF            0xd4
+#define SBC_SAMPLE_RATE        0x40
+#define SBC_MONO_8BIT          0xa0
+#define SBC_MONO_16BIT         0xa4
+#define SBC_STEREO_8BIT        0xa8
+#define SBC_STEREO_16BIT       0xac
+
+#define SBC4_OUT8_AI           0xc6
+#define SBC4_IN8_AI            0xce
+#define SBC4_MODE_UNS_MONO     0x00
+
+/* --------------------------------------------------------------------- */
+
+static int inline reset_dsp(struct device *dev)
+{
+	int i;
+
+	outb(1, DSP_RESET(dev->base_addr));
+	for (i = 0; i < 0x100; i++)
+		SLOW_DOWN_IO;
+	outb(0, DSP_RESET(dev->base_addr));
+	for (i = 0; i < 0xffff; i++)
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
+			if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
+				return 1;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline write_dsp(struct device *dev, unsigned char data)
+{
+	int i;
+	
+	for (i = 0; i < 0xffff; i++)
+		if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
+			outb(data, DSP_WRITE_DATA(dev->base_addr));
+			return;
+		}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline read_dsp(struct device *dev, unsigned char *data)
+{
+	int i;
+
+	if (!data)
+		return 0;
+	for (i = 0; i < 0xffff; i++) 
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
+			*data = inb(DSP_READ_DATA(dev->base_addr));
+			return 1;
+		}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack(struct device *dev)
+{
+	inb(DSP_DATA_AVAIL(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send)
+{
+        unsigned long flags;
+        static const unsigned char sbcmode[2][2] = {
+		{ SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, 
+		{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }
+	};
+	static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
+        static const unsigned char dmamode[2] = { 
+		DMA_MODE_READ | DMA_MODE_AUTOINIT, DMA_MODE_WRITE  | DMA_MODE_AUTOINIT
+	};
+	static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
+	unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf);
+
+	send = !!send;
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+                return;
+        }
+        if ((dmabufaddr & 0xffff) + SCSTATE->dmabuflen > 0x10000)
+                panic("sm: DMA buffer violates DMA boundary!");
+        save_flags(flags);
+        cli();
+        sbc_int_ack(dev);
+        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
+        write_dsp(dev, SCSTATE->fmt[send]);
+        write_dsp(dev, sbcskr[send]); 
+        disable_dma(dev->dma);
+        clear_dma_ff(dev->dma);
+        set_dma_mode(dev->dma, dmamode[send]);
+        set_dma_addr(dev->dma, dmabufaddr);
+        set_dma_count(dev->dma, SCSTATE->dmabuflen);
+        enable_dma(dev->dma);
+        sbc_int_ack(dev);
+	if (SCSTATE->revhi >= 4) {
+		write_dsp(dev, sbc4mode[send]);
+		write_dsp(dev, SBC4_MODE_UNS_MONO);
+		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+	} else {
+		write_dsp(dev, SBC_BLOCKSIZE);
+		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+		write_dsp(dev, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+		write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
+		/* hispeed mode if sample rate > 13kHz */
+	}
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned char new_ptt;
+	unsigned char *buf;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	new_ptt = hdlcdrv_ptt(&sm->hdrv);
+ 	sbc_int_ack(dev);
+ 	buf = SCSTATE->dmabuf;
+	if (SCSTATE->dmabufidx)
+		buf += SCSTATE->dmabuflen/2;
+	SCSTATE->dmabufidx = !SCSTATE->dmabufidx;
+	sm_int_freq(sm);
+	sti();
+	if (new_ptt && !SCSTATE->ptt) {
+		/* starting to transmit */
+		disable_dma(dev->dma);
+ 		SCSTATE->dmabufidx = 0;
+		time_exec(sm->debug_vals.demod_cyc, 
+			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf, 
+						 SCSTATE->dmabuflen/2));
+ 		setup_dma_dsp(dev, sm, 1);
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf + 
+						 SCSTATE->dmabuflen/2,
+						 SCSTATE->dmabuflen/2));
+	} else if (SCSTATE->ptt == 1 && !new_ptt) {
+		/* stopping transmission */
+		disable_dma(dev->dma);
+ 		SCSTATE->dmabufidx = 0;
+		setup_dma_dsp(dev, sm, 0);
+		SCSTATE->ptt = 0;
+	} else if (SCSTATE->ptt) {
+                SCSTATE->ptt--;
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator(sm, buf, SCSTATE->dmabuflen/2));
+        } else {
+		time_exec(sm->debug_vals.demod_cyc, 
+			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+        }
+        if (new_ptt)
+                SCSTATE->ptt = 2;
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_open(struct device *dev, struct sm_state *sm) 
+{
+	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
+		       sizeof(struct sc_state_sbc), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SBC_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (!reset_dsp(dev))
+		return -ENODEV;
+	write_dsp(dev, SBC_GET_REVISION);
+	if (!read_dsp(dev, &SCSTATE->revhi) || 
+	    !read_dsp(dev, &SCSTATE->revlo))
+		return -ENODEV;
+	if (SCSTATE->revhi < 2) {
+		printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
+		       "rev 2.00 required\n", sm_drvname);
+		return -ENODEV;
+	}
+	if (SCSTATE->revhi < 3 && 
+	    (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
+		printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
+		       "old, at least 3.00 required\n", sm_drvname,
+		       dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
+		return -ENODEV;
+	}
+	/*
+	 * initialize some variables
+	 */
+	if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	SCSTATE->dmabufidx = SCSTATE->ptt = 0;
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_dsp(dev, sm, 0);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	reset_dsp(dev);	
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, SBC_EXTENT);
+	kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int dv;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/
+							 sm->mode_rx->srate);
+				SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/
+							 sm->mode_tx->srate);
+				dv = lcm(sm->mode_tx->dmabuflenmodulo, 
+					 sm->mode_rx->dmabuflenmodulo);
+				SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1;
+				SCSTATE->dmabuflen /= dv;
+				SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+		     struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_ioctl bi;
+	unsigned long flags;
+	int i;
+	
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case SMCTL_GETMIXER:
+		i = 0;
+		bi.data.mix.sample_rate = sm->mode_rx->srate;
+		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+		switch (SCSTATE->revhi) {
+		case 2:
+			bi.data.mix.mixer_type = SM_MIXER_CT1335;
+			break;
+		case 3:
+			bi.data.mix.mixer_type = SM_MIXER_CT1345;
+			break;
+		case 4:
+			bi.data.mix.mixer_type = SM_MIXER_CT1745;
+			break;
+		}
+		if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
+		    bi.data.mix.reg < 0x80) {
+			save_flags(flags);
+			cli();
+			outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+			bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
+			restore_flags(flags);
+			i = 1;
+		}
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return i;
+		
+	case SMCTL_SETMIXER:
+		if (!suser())
+			return -EACCES;
+		switch (SCSTATE->revhi) {
+		case 2:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
+				return -EINVAL;
+			break;
+		case 3:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
+				return -EINVAL;
+			break;
+		case 4:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
+				return -EINVAL;
+			break;
+		default:
+			return -ENODEV;
+		}
+		if (bi.data.mix.reg >= 0x80)
+			return -EACCES;
+		save_flags(flags);
+		cli();
+		outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+		outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
+		restore_flags(flags);
+		return 0;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbc = {
+	"sbc", sizeof(struct sc_state_sbc), 
+	sbc_open, sbc_close, sbc_ioctl, sbc_sethw
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem/sm_wss.c linux/drivers/net/soundmodem/sm_wss.c
--- v2.1.26/linux/drivers/net/soundmodem/sm_wss.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/soundmodem/sm_wss.c	Fri Feb 21 14:58:35 1997
@@ -0,0 +1,948 @@
+/*****************************************************************************/
+
+/*
+ *	sm_wss.c  -- soundcard radio modem driver, WSS (half duplex) driver
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include "sm.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_wss {
+	unsigned char revwss, revid, revv, revcid;
+	unsigned char fmt[2];
+	unsigned char crystal;
+	unsigned int dmabuflen;
+	unsigned char *dmabuf;
+	unsigned char dmabufidx;
+	unsigned char ptt;
+	/* Full Duplex extensions */
+	unsigned char *dmabuf2;
+};
+
+#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+
+#define WSS_CONFIG(iobase)       (iobase+0)
+#define WSS_STATUS(iobase)       (iobase+3)
+#define WSS_CODEC_IA(iobase)     (iobase+4)
+#define WSS_CODEC_ID(iobase)     (iobase+5)
+#define WSS_CODEC_STATUS(iobase) (iobase+6)
+#define WSS_CODEC_DATA(iobase)   (iobase+7)
+
+#define WSS_EXTENT   8
+
+/* --------------------------------------------------------------------- */
+
+static void write_codec(struct device *dev, unsigned char idx,
+			unsigned char data)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx, WSS_CODEC_IA(dev->base_addr));
+	outb(data, WSS_CODEC_ID(dev->base_addr));
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct device *dev, unsigned char idx)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr));
+	return inb(WSS_CODEC_ID(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern void inline wss_ack_int(struct device *dev)
+{
+	outb(0, WSS_CODEC_STATUS(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_srate_tab[16] = {
+	8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
+	-1, 37800, -1, 44100, 48000, 33075, 9600, 6620
+};
+
+static int wss_srate_index(int srate)
+{
+	int i;
+
+	for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
+		if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
+			return i;
+	return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, 
+			     unsigned char fmt, char fdx, char fullcalib)
+{
+	unsigned long time;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/* Clock and data format register */
+	write_codec(dev, 0x48, fmt);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 0x5c, fmt & 0xf0);
+		/* MCE and interface config reg */	
+		write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
+	} else 
+		/* MCE and interface config reg */
+		write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
+	outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
+	/*
+	 * wait for ACI start
+	 */
+	time = 1000;
+	while (!(read_codec(dev, 0x0b) & 0x20))
+		if (!(--time)) {
+			printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", 
+			       sm_drvname);
+			restore_flags(flags);
+			return -1;
+		}
+	/*
+	 * wait for ACI end
+	 */
+	sti();
+	time = jiffies + HZ/4;
+	while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
+	restore_flags(flags);
+	if ((signed)(jiffies - time) >= 0) {
+		printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", 
+		       sm_drvname);
+		return -1;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, 
+			  unsigned char src_l, unsigned char src_r, 
+			  int igain_l, int igain_r,
+			  int ogain_l, int ogain_r)
+{
+	unsigned char tmp, reg0, reg1, reg6, reg7;
+	static const signed char irqtab[16] = 
+	{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
+		  -1, -1 };
+	static const signed char dmatab[4] = { 1, 2, -1, 3 };
+	
+	tmp = inb(WSS_STATUS(dev->base_addr));
+	if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
+	    (tmp & 0x3f) != 0x0f) {
+		printk(KERN_WARNING "sm: WSS card id register not found, "
+		       "address 0x%lx, ID register 0x%02x\n", 
+		       dev->base_addr, (int)tmp);
+		/* return -1; */
+		SCSTATE->revwss = 0;
+	} else {
+		if ((tmp & 0x80) && ((dev->dma == 0) || 
+				     ((dev->irq >= 8) && (dev->irq != 9)))) {
+			printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
+			       "(except IRQ9) cannot be used on an 8bit "
+			       "card\n", sm_drvname);
+			return -1;
+		}		
+		if (dev->irq > 15 || irqtab[dev->irq] == -1) {
+			printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", 
+			       sm_drvname, (int)dev->irq);
+			return -1;
+		}
+		if (dev->dma > 3 || dmatab[dev->dma] == -1) {
+			printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", 
+			       sm_drvname, (int)dev->dma);
+			return -1;
+		}
+		tmp = irqtab[dev->irq] | dmatab[dev->dma];
+		/* irq probe */
+		outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
+		if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
+			outb(0, WSS_CONFIG(dev->base_addr));
+			printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", 
+			       sm_drvname, dev->irq);
+		}
+		outb(tmp, WSS_CONFIG(dev->base_addr));
+		SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
+	}
+	/*
+	 * initialize the codec
+	 */
+	if (igain_l < 0)
+		igain_l = 0;
+	if (igain_r < 0)
+		igain_r = 0;
+	if (ogain_l > 0)
+		ogain_l = 0;
+	if (ogain_r > 0)
+		ogain_r = 0;
+	reg0 = (src_l << 6) & 0xc0;
+	reg1 = (src_r << 6) & 0xc0;
+	if (reg0 == 0x80 && igain_l >= 20) {
+		reg0 |= 0x20;
+		igain_l -= 20;
+	}
+	if (reg1 == 0x80 && igain_r >= 20) {
+		reg1 |= 0x20;
+		igain_r -= 20;
+	}
+	if (igain_l > 23)
+		igain_l = 23;
+	if (igain_r > 23)
+		igain_r = 23;
+	reg0 |= igain_l * 2 / 3;
+	reg1 |= igain_r * 2 / 3;
+	reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
+	reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
+	write_codec(dev, 9, 0);
+	write_codec(dev, 0, 0x45);
+	if (read_codec(dev, 0) != 0x45)
+		goto codec_err;
+	write_codec(dev, 0, 0xaa);
+	if (read_codec(dev, 0) != 0xaa)
+		goto codec_err;
+	write_codec(dev, 12, 0x40); /* enable MODE2 */
+	write_codec(dev, 16, 0);
+	write_codec(dev, 0, 0x45);
+	SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
+	write_codec(dev, 0, 0xaa);
+	SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
+	if (SCSTATE->crystal) {
+		SCSTATE->revcid = read_codec(dev, 0x19);
+		SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
+		SCSTATE->revcid &= 7;
+		write_codec(dev, 0x10, 0x80); /* maximum output level */
+		write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
+		write_codec(dev, 0x12, 0x80); /* left line input control */
+		write_codec(dev, 0x13, 0x80); /* right line input control */
+		write_codec(dev, 0x16, 0); /* disable alternative freq sel */
+		write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
+		write_codec(dev, 0x1b, 0x00); /* left out no att */
+		write_codec(dev, 0x1d, 0x00); /* right out no att */
+	}
+
+	if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], fdx, 1))
+		goto codec_err;
+
+        write_codec(dev, 0, reg0); /* left input control */
+        write_codec(dev, 1, reg1); /* right input control */
+        write_codec(dev, 2, 0x80); /* left aux#1 input control */
+        write_codec(dev, 3, 0x80); /* right aux#1 input control */
+        write_codec(dev, 4, 0x80); /* left aux#2 input control */
+        write_codec(dev, 5, 0x80); /* right aux#2 input control */
+        write_codec(dev, 6, reg6); /* left dac control */
+        write_codec(dev, 7, reg7); /* right dac control */
+        write_codec(dev, 0xa, 0x2); /* pin control register */
+        write_codec(dev, 0xd, 0x0); /* digital mix control */
+	SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
+	/*
+	 * print revisions
+	 */
+	if (SCSTATE->crystal) 
+		printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
+		       " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
+		       (int)SCSTATE->revv, (int)SCSTATE->revcid);
+	else
+		printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", 
+		       sm_drvname, (int)SCSTATE->revwss, 
+		       (int)SCSTATE->revid);
+	return 0;
+ codec_err:
+	outb(0, WSS_CONFIG(dev->base_addr));
+	printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", 
+	       sm_drvname, dev->base_addr);
+	return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send)
+{
+        unsigned long flags;
+        static const unsigned char codecmode[2] = { 0x0e, 0x0d };
+        static const unsigned char dmamode[2] = { 
+		DMA_MODE_READ | DMA_MODE_AUTOINIT, 
+		DMA_MODE_WRITE  | DMA_MODE_AUTOINIT
+	};
+	unsigned char oldcodecmode;
+	long abrt;
+	unsigned long dmabufaddr = virt_to_bus(SCSTATE->dmabuf);
+
+        if ((dmabufaddr & 0xffffu) + SCSTATE->dmabuflen > 0x10000)
+                panic("%s: DMA buffer violates DMA boundary!", sm_drvname);
+	send = !!send;
+        save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+	oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	wss_ack_int(dev);
+	if (read_codec(dev, 11) & 0x10) {
+		disable_dma(dev->dma);
+		clear_dma_ff(dev->dma);
+		set_dma_mode(dev->dma, dmamode[oldcodecmode & 1]);
+		set_dma_addr(dev->dma, dmabufaddr);
+		set_dma_count(dev->dma, SCSTATE->dmabuflen);
+		enable_dma(dev->dma);
+		abrt = 0;
+		while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
+	}
+        disable_dma(dev->dma);
+	if (read_codec(dev, 0x8) != SCSTATE->fmt[send])
+		wss_set_codec_fmt(dev, sm, SCSTATE->fmt[send], 0, 0);
+        clear_dma_ff(dev->dma);
+        set_dma_mode(dev->dma, dmamode[send]);
+        set_dma_addr(dev->dma, dmabufaddr);
+        set_dma_count(dev->dma, SCSTATE->dmabuflen);
+        enable_dma(dev->dma);
+	write_codec(dev, 15, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+	write_codec(dev, 14, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+		write_codec(dev, 30, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+	}
+	write_codec(dev, 9, codecmode[send]);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned char new_ptt;
+	unsigned char *buf;
+	int dmares;
+
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
+	    sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	new_ptt = hdlcdrv_ptt(&sm->hdrv);
+	cli();
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	dmares = get_dma_residue(dev->dma);
+	if (dmares <= 0)
+		dmares = SCSTATE->dmabuflen;
+	buf = SCSTATE->dmabuf;
+	if (dmares > SCSTATE->dmabuflen/2) {
+		buf += SCSTATE->dmabuflen/2;
+		dmares -= SCSTATE->dmabuflen/2;
+	}
+#ifdef SM_DEBUG
+	if (!sm->debug_vals.dma_residue || 
+	    dmares < sm->debug_vals.dma_residue)
+		sm->debug_vals.dma_residue = dmares;
+#endif /* SM_DEBUG */
+	dmares--;
+	write_codec(dev, 15, dmares & 0xff);
+	write_codec(dev, 14, dmares >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, dmares & 0xff);
+		write_codec(dev, 30, dmares >> 8);
+	}
+	enable_dma(dev->dma);
+	sm_int_freq(sm);
+	sti();
+	if (new_ptt && !SCSTATE->ptt) {
+		/* starting to transmit */
+		disable_dma(dev->dma);
+		sti();
+ 		SCSTATE->dmabufidx = 0;
+		time_exec(sm->debug_vals.demod_cyc, 
+			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf, 
+						 SCSTATE->dmabuflen/2));
+ 		setup_dma_wss(dev, sm, 1);
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator(sm, SCSTATE->dmabuf + 
+						 SCSTATE->dmabuflen/2,
+						 SCSTATE->dmabuflen/2));
+	} else if (SCSTATE->ptt == 1 && !new_ptt) {
+		/* stopping transmission */
+		disable_dma(dev->dma);
+		sti();
+ 		SCSTATE->dmabufidx = 0;
+		setup_dma_wss(dev, sm, 0);
+		SCSTATE->ptt = 0;
+	} else if (SCSTATE->ptt) {
+                SCSTATE->ptt--;
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator(sm, buf, SCSTATE->dmabuflen/2));
+        } else {
+		time_exec(sm->debug_vals.demod_cyc, 
+			  sm->mode_rx->demodulator(sm, buf, SCSTATE->dmabuflen/2));
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+        }
+        if (new_ptt)
+                SCSTATE->ptt = 2;
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_open(struct device *dev, struct sm_state *sm) 
+{
+	if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
+		printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", 
+		       sizeof(struct sc_state_wss), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	SCSTATE->dmabufidx = SCSTATE->ptt = 0;
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_wss(dev, sm, 0);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int i, j, dv;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    ((j = wss_srate_index((*mrp)->srate)) >= 0)) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = j;
+				SCSTATE->fmt[1] = i;
+				dv = lcm(sm->mode_tx->dmabuflenmodulo, 
+					 sm->mode_rx->dmabuflenmodulo);
+				SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1;
+				SCSTATE->dmabuflen /= dv;
+				SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+		     struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_ioctl bi;
+	int i;
+	
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case SMCTL_GETMIXER:
+		i = 0;
+		bi.data.mix.sample_rate = sm->mode_rx->srate;
+		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+		bi.data.mix.mixer_type = SCSTATE->crystal ? 
+			SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
+		if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) 
+		     >> bi.data.mix.reg) & 1) {
+			bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
+			i = 1;
+		}
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return i;
+
+	case SMCTL_SETMIXER:
+		if (!suser())
+			return -EACCES;
+		if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || 
+		     !SCSTATE->crystal) &&
+		    (bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
+		     bi.data.mix.reg >= 0x10))
+			return -EINVAL;
+		if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
+			return -EACCES;
+		write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
+		return 0;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wss = {
+	"wss", sizeof(struct sc_state_wss), 
+	wss_open, wss_close, wss_ioctl, wss_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm)
+{
+        unsigned long flags;
+	unsigned char oldcodecmode, codecdma;
+	long abrt;
+	unsigned long dmabufaddr1 = virt_to_bus(SCSTATE->dmabuf);
+	unsigned long dmabufaddr2 = virt_to_bus(SCSTATE->dmabuf2);
+
+        if (((dmabufaddr1 & 0xffffu) + SCSTATE->dmabuflen > 0x10000) ||
+	    ((dmabufaddr2 & 0xffffu) + SCSTATE->dmabuflen > 0x10000))
+                panic("%s: DMA buffer violates DMA boundary!", sm_drvname);
+        save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+	oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0); /* disable codec DMA */
+	wss_ack_int(dev);
+	if ((codecdma = read_codec(dev, 11)) & 0x10) {
+		disable_dma(dev->dma);
+		disable_dma(sm->hdrv.ptt_out.dma2);
+		clear_dma_ff(dev->dma);
+		set_dma_mode(dev->dma, DMA_MODE_WRITE  | DMA_MODE_AUTOINIT);
+		set_dma_addr(dev->dma, dmabufaddr1);
+		set_dma_count(dev->dma, SCSTATE->dmabuflen);
+		clear_dma_ff(sm->hdrv.ptt_out.dma2);
+		set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_READ  | DMA_MODE_AUTOINIT);
+		set_dma_addr(sm->hdrv.ptt_out.dma2, dmabufaddr2);
+		set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen);
+		enable_dma(dev->dma);
+		enable_dma(sm->hdrv.ptt_out.dma2);
+		abrt = 0;
+		while (((codecdma = read_codec(dev, 11)) & 0x10) ||
+		       ((++abrt) >= 0x10000));
+	}
+        disable_dma(dev->dma);
+        disable_dma(sm->hdrv.ptt_out.dma2);
+	clear_dma_ff(dev->dma);
+	set_dma_mode(dev->dma, DMA_MODE_WRITE  | DMA_MODE_AUTOINIT);
+	set_dma_addr(dev->dma, dmabufaddr1);
+	set_dma_count(dev->dma, SCSTATE->dmabuflen);
+	clear_dma_ff(sm->hdrv.ptt_out.dma2);
+	set_dma_mode(sm->hdrv.ptt_out.dma2, DMA_MODE_READ  | DMA_MODE_AUTOINIT);
+	set_dma_addr(sm->hdrv.ptt_out.dma2, dmabufaddr2);
+	set_dma_count(sm->hdrv.ptt_out.dma2, SCSTATE->dmabuflen);
+        enable_dma(dev->dma);
+        enable_dma(sm->hdrv.ptt_out.dma2);
+	write_codec(dev, 15, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+	write_codec(dev, 14, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, ((SCSTATE->dmabuflen >> 1) - 1) & 0xff);
+		write_codec(dev, 30, ((SCSTATE->dmabuflen >> 1) - 1) >> 8);
+	}
+	write_codec(dev, 9, 3);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned char *buf1;
+	unsigned char *buf2;
+	unsigned long flags;
+	int dmares1, dmares2;
+
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
+	    sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	save_flags(flags);
+	cli();
+	if (SCSTATE->crystal && (!(read_codec(dev, 0x18) & 0x20))) {
+		/* only regard Crystal Playback interrupts! */
+		wss_ack_int(dev);
+		return;
+	}
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+	clear_dma_ff(dev->dma);
+	dmares1 = get_dma_residue(dev->dma);
+	clear_dma_ff(sm->hdrv.ptt_out.dma2);
+	dmares2 = get_dma_residue(sm->hdrv.ptt_out.dma2);
+	if (dmares1 <= 0)
+		dmares1 = SCSTATE->dmabuflen;
+	buf1 = SCSTATE->dmabuf;
+	if (dmares1 > SCSTATE->dmabuflen/2) {
+		buf1 += SCSTATE->dmabuflen/2;
+		dmares1 -= SCSTATE->dmabuflen/2;
+	}
+	if (dmares2 <= 0)
+		dmares2 = SCSTATE->dmabuflen;
+	buf2 = SCSTATE->dmabuf2;
+	if (dmares2 > SCSTATE->dmabuflen/2) {
+		buf2 += SCSTATE->dmabuflen/2;
+		dmares2 -= SCSTATE->dmabuflen/2;
+	}
+#ifdef SM_DEBUG
+	if (!sm->debug_vals.dma_residue || 
+	    dmares1 < sm->debug_vals.dma_residue)
+		sm->debug_vals.dma_residue = dmares1;
+#endif /* SM_DEBUG */
+	dmares1--;
+	dmares2--;
+	write_codec(dev, 15, dmares1 & 0xff);
+	write_codec(dev, 14, dmares1 >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, dmares2 & 0xff);
+		write_codec(dev, 30, dmares2 >> 8);
+	}
+	enable_dma(dev->dma);
+	enable_dma(sm->hdrv.ptt_out.dma2);
+	restore_flags(flags);
+	sm_int_freq(sm);
+	sti();
+	if ((SCSTATE->ptt = hdlcdrv_ptt(&sm->hdrv))) 
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator(sm, buf1, SCSTATE->dmabuflen/2));
+	else
+		time_exec(sm->debug_vals.mod_cyc, 
+			  memset(buf1, 0x80, SCSTATE->dmabuflen/2));
+	time_exec(sm->debug_vals.demod_cyc, 
+		  sm->mode_rx->demodulator(sm, buf2, SCSTATE->dmabuflen/2));
+	hdlcdrv_arbitrate(dev, &sm->hdrv);
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_open(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	if (!(SCSTATE->dmabuf = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	if (!(SCSTATE->dmabuf2 = kmalloc(SCSTATE->dmabuflen, GFP_KERNEL | GFP_DMA))) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		return -ENOMEM;
+	}
+	SCSTATE->dmabufidx = SCSTATE->ptt = 0;
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+		return -EBUSY;
+	}
+	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+		free_dma(dev->dma);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+		kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+		free_dma(dev->dma);
+		free_dma(sm->hdrv.ptt_out.dma2);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+	setup_fdx_dma_wss(dev, sm);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	free_dma(sm->hdrv.ptt_out.dma2);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree_s(SCSTATE->dmabuf, SCSTATE->dmabuflen);
+	kfree_s(SCSTATE->dmabuf2, SCSTATE->dmabuflen);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int i, dv;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mtp)->srate == (*mrp)->srate) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
+				dv = lcm(sm->mode_tx->dmabuflenmodulo, 
+					 sm->mode_rx->dmabuflenmodulo);
+				SCSTATE->dmabuflen = sm->mode_rx->srate/100+dv-1;
+				SCSTATE->dmabuflen /= dv;
+				SCSTATE->dmabuflen *= 2*dv; /* make sure DMA buf is even */
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
+			HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
+			HDLCDRV_PARMASK_MIDIIOBASE;
+
+	return wss_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wssfdx = {
+	"wssfdx", sizeof(struct sc_state_wss), 
+	wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+#undef SCSTATE
diff -u --recursive --new-file v2.1.26/linux/drivers/net/soundmodem.c linux/drivers/net/soundmodem.c
--- v2.1.26/linux/drivers/net/soundmodem.c	Tue Dec 31 00:30:04 1996
+++ linux/drivers/net/soundmodem.c	Wed Dec 31 16:00:00 1969
@@ -1,1022 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	soundmodem.c  -- soundcard radio modem driver.
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  Command line options (insmod command line)
- *
- *  hardware hardware type; 0=sbc, 1=wss, any other value invalid
- *  mode     mode type; 0=1200 baud AFSK, 1=9600 baud FSK, any other
- *           value invalid
- *  iobase   base address of the soundcard; common values are 0x220 for sbc,
- *           0x530 for wss
- *  irq      interrupt number; common values are 7 or 5 for sbc, 11 for wss
- *  dma      dma number; common values are 0 or 1
- *
- *
- *  History:
- *   0.1  21.09.96  Started
- *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-
-#include <linux/config.h>
-#include <linux/ptrace.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/net.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <limits.h>
-#include <linux/hdlcdrv.h>
-#include <linux/soundmodem.h>
-
-/* --------------------------------------------------------------------- */
-
-static const char sm_drvname[] = "soundmodem";
-static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "soundmodem: version 0.3 compiled " __TIME__ " " __DATE__ "\n";           ;
-
-/* --------------------------------------------------------------------- */
-
-#define NR_PORTS 4
-
-#define SM_DEBUG
-
-/* --------------------------------------------------------------------- */
-
-static struct device sm_device[NR_PORTS];
-
-static struct {
-	char *mode;
-	int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase;
-} sm_ports[NR_PORTS] = {
-	{ NULL, -1, 0, 0, 0, -1, -1, -1 },
-};
-
-/* --------------------------------------------------------------------- */
-
-#define DMA_MODE_AUTOINIT      0x10
-
-/* --------------------------------------------------------------------- */
-
-#define UART_RBR(iobase) (iobase+0)
-#define UART_THR(iobase) (iobase+0)
-#define UART_IER(iobase) (iobase+1)
-#define UART_IIR(iobase) (iobase+2)
-#define UART_FCR(iobase) (iobase+2)
-#define UART_LCR(iobase) (iobase+3)
-#define UART_MCR(iobase) (iobase+4)
-#define UART_LSR(iobase) (iobase+5)
-#define UART_MSR(iobase) (iobase+6)
-#define UART_SCR(iobase) (iobase+7)
-#define UART_DLL(iobase) (iobase+0)
-#define UART_DLM(iobase) (iobase+1)
-
-#define SER_EXTENT 8
-
-#define LPT_DATA(iobase)    (iobase+0)
-#define LPT_STATUS(iobase)  (iobase+1)
-#define LPT_CONTROL(iobase) (iobase+2)
-#define LPT_IRQ_ENABLE      0x10
-
-#define LPT_EXTENT 3
-
-#define MIDI_DATA(iobase)     (iobase)
-#define MIDI_STATUS(iobase)   (iobase+1)
-#define MIDI_READ_FULL 0x80   /* attention: negative logic!! */
-#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
-
-#define MIDI_EXTENT 2
-
-/* ---------------------------------------------------------------------- */
-
-#define PARAM_TXDELAY   1
-#define PARAM_PERSIST   2
-#define PARAM_SLOTTIME  3
-#define PARAM_TXTAIL    4
-#define PARAM_FULLDUP   5
-#define PARAM_HARDWARE  6
-#define PARAM_RETURN    255
-
-#define SP_SER  1
-#define SP_PAR  2
-#define SP_MIDI 4
-
-/* ---------------------------------------------------------------------- */
-/*
- * Information that need to be kept for each board.
- */
-
-struct sm_state {
-	struct hdlcdrv_state hdrv;
-
-	const struct modem_tx_info *mode_tx;
-	const struct modem_rx_info *mode_rx;
-
-	const struct hardware_info *hwdrv;
-
-	/*
-	 * Hardware (soundcard) access routines state
-	 */
-	union {
-		long hw[32/sizeof(long)];
-	} hw;
-
-	/*
-	 * state of the modem code
-	 */
-	union {
-		long m[32/sizeof(long)];
-	} m;
-	union {
-		long d[256/sizeof(long)];
-	} d;
-
-#define DIAGDATALEN 64
-	struct diag_data {
-		unsigned int mode;
-		unsigned int flags;
-		volatile int ptr;
-		short data[DIAGDATALEN];
-	} diag;
-
-
-#ifdef SM_DEBUG
-	struct debug_vals {
-		unsigned long last_jiffies;
-		unsigned cur_intcnt;
-		unsigned last_intcnt;
-		unsigned mod_cyc;
-		unsigned demod_cyc;
-		unsigned dma_residue;
-	} debug_vals;
-#endif /* SM_DEBUG */
-};
-
-/* ---------------------------------------------------------------------- */
-/*
- * Mode definition structure
- */
-
-struct modem_tx_info {
-	const struct modem_tx_info *next;
-	const char *name;
-	unsigned int loc_storage;
-	int srate;
-	int bitrate;
-	unsigned int dmabuflenmodulo;
-	void (*modulator)(struct sm_state *, unsigned char *, int);
-	void (*init)(struct sm_state *);
-};
-#define NEXT_TX_INFO NULL
-
-extern const struct modem_tx_info *modem_tx_base;
-
-struct modem_rx_info {
-	const struct modem_rx_info *next;
-	const char *name;
-	unsigned int loc_storage;
-	int srate;
-	int bitrate;
-	unsigned int dmabuflenmodulo;
-	unsigned int sperbit;
-	void (*demodulator)(struct sm_state *, unsigned char *, int);
-	void (*init)(struct sm_state *);
-};
-#define NEXT_RX_INFO NULL
-
-extern const struct modem_rx_info *modem_rx_base;
-
-/* ---------------------------------------------------------------------- */
-/*
- * Soundcard driver definition structure
- */
-
-struct hardware_info {
-	const struct hardware_info *next;
-	char *hw_name; /* used for request_{region,irq,dma} */
-	unsigned int loc_storage;
-	/*
-	 * mode specific open/close
-	 */
-	int (*open)(struct device *, struct sm_state *);
-	int (*close)(struct device *, struct sm_state *);
-	int (*ioctl)(struct device *, struct sm_state *, struct ifreq *,
-		     struct hdlcdrv_ioctl *, int);
-	int (*sethw)(struct device *, struct sm_state *, char *);
-};
-#define NEXT_HW_INFO NULL
-
-extern const struct hardware_info *hardware_base;
-
-/* --------------------------------------------------------------------- */
-
-#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-
-/* --------------------------------------------------------------------- */
-
-static void inline sm_int_freq(struct sm_state *sm)
-{
-#ifdef SM_DEBUG
-	unsigned long cur_jiffies = jiffies;
-	/*
-	 * measure the interrupt frequency
-	 */
-	sm->debug_vals.cur_intcnt++;
-	if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
-		sm->debug_vals.last_jiffies = cur_jiffies;
-		sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
-		sm->debug_vals.cur_intcnt = 0;
-	}
-#endif /* SM_DEBUG */
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== port checking routines ========================
- */
-
-static int check_lpt(unsigned int iobase)
-{
-	unsigned char b1,b2;
-	int i;
-
-	if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
-		return 0;
-	if (check_region(iobase, LPT_EXTENT))
-		return 0;
-	b1 = inb(LPT_DATA(iobase));
-	b2 = inb(LPT_CONTROL(iobase));
-	outb(0xaa, LPT_DATA(iobase));
-	i = inb(LPT_DATA(iobase)) == 0xaa;
-	outb(0x55, LPT_DATA(iobase));
-	i &= inb(LPT_DATA(iobase)) == 0x55;
-	outb(0x0a, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
-	outb(0x05, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
-	outb(b1, LPT_DATA(iobase));
-	outb(b2, LPT_CONTROL(iobase));
-	return !i;
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
-	c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
-	{ "unknown", "8250", "16450", "16550", "16550A" };
-
-static enum uart check_uart(unsigned int iobase)
-{
-	unsigned char b1,b2,b3;
-	enum uart u;
-	enum uart uart_tab[] =
-		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
-	if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
-		return c_uart_unknown;
-	if (check_region(iobase, SER_EXTENT))
-		return c_uart_unknown;
-	b1 = inb(UART_MCR(iobase));
-	outb(b1 | 0x10, UART_MCR(iobase));	/* loopback mode */
-	b2 = inb(UART_MSR(iobase));
-	outb(0x1a, UART_MCR(iobase));
-	b3 = inb(UART_MSR(iobase)) & 0xf0;
-	outb(b1, UART_MCR(iobase));	   /* restore old values */
-	outb(b2, UART_MSR(iobase));
-	if (b3 != 0x90)
-		return c_uart_unknown;
-	inb(UART_RBR(iobase));
-	inb(UART_RBR(iobase));
-	outb(0x01, UART_FCR(iobase));		/* enable FIFOs */
-	u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
-	if (u == c_uart_16450) {
-		outb(0x5a, UART_SCR(iobase));
-		b1 = inb(UART_SCR(iobase));
-		outb(0xa5, UART_SCR(iobase));
-		b2 = inb(UART_SCR(iobase));
-		if ((b1 != 0x5a) || (b2 != 0xa5))
-			u = c_uart_8250;
-	}
-	return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int check_midi(unsigned int iobase)
-{
-	unsigned long timeout;
-	unsigned long flags;
-	unsigned char b;
-
-	if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
-		return 0;
-	if (check_region(iobase, MIDI_EXTENT))
-		return 0;
-	timeout = jiffies + (HZ / 100);
-	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
-		if ((signed)(jiffies - timeout) > 0)
-			return 0;
-	save_flags(flags);
-	cli();
-	outb(0xff, MIDI_DATA(iobase));
-	b = inb(MIDI_STATUS(iobase));
-	restore_flags(flags);
-	if (!(b & MIDI_WRITE_EMPTY))
-		return 0;
-	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
-		if ((signed)(jiffies - timeout) > 0)
-			return 0;
-	return 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-extern void inline output_status(struct sm_state *sm)
-{
-	int invert_dcd = 0;
-	int invert_ptt = 0;
-
-	int ptt = hdlcdrv_ptt(&sm->hdrv) ^ invert_ptt;
-	int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
-
-	if (sm->hdrv.ptt_out.flags & SP_SER) {
-		outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
-		outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
-	}
-	if (sm->hdrv.ptt_out.flags & SP_PAR) {
-		outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
-	}
-	if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
-		outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void output_open(struct sm_state *sm)
-{
-	enum uart u = c_uart_unknown;
-
-	sm->hdrv.ptt_out.flags = 0;
-	if (sm->hdrv.ptt_out.seriobase > 0 &&
-	    sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
-	    ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
-		sm->hdrv.ptt_out.flags |= SP_SER;
-		request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
-		outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
-		/* 5 bits, 1 stop, no parity, no break, Div latch access */
-		outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
-		outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
-		outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
-		/* LCR and MCR set by output_status */
-	}
-	if (sm->hdrv.ptt_out.pariobase > 0 &&
-	    sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
-	    check_lpt(sm->hdrv.ptt_out.pariobase)) {
-		sm->hdrv.ptt_out.flags |= SP_PAR;
-		request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
-	}
-	if (sm->hdrv.ptt_out.midiiobase > 0 &&
-	    sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
-	    check_midi(sm->hdrv.ptt_out.midiiobase)) {
-		sm->hdrv.ptt_out.flags |= SP_MIDI;
-		request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
-			       "sm midi ptt");
-	}
-	output_status(sm);
-
-	printk(KERN_INFO "%s: ptt output:", sm_drvname);
-	if (sm->hdrv.ptt_out.flags & SP_SER)
-		printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
-		       uart_str[u]);
-	if (sm->hdrv.ptt_out.flags & SP_PAR)
-		printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
-	if (sm->hdrv.ptt_out.flags & SP_MIDI)
-		printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
-	if (!sm->hdrv.ptt_out.flags)
-		printk(" none");
-	printk("\n");
-}
-
-/* --------------------------------------------------------------------- */
-
-static void output_close(struct sm_state *sm)
-{
-	/* release regions used for PTT output */
-	sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
-	output_status(sm);
-	if (sm->hdrv.ptt_out.flags & SP_SER)
-		release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
-       	if (sm->hdrv.ptt_out.flags & SP_PAR)
-		release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
-       	if (sm->hdrv.ptt_out.flags & SP_MIDI)
-		release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
-	sm->hdrv.ptt_out.flags = 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== diagnostics stuff ===============================
- */
-
-extern inline void diag_trigger(struct sm_state *sm)
-{
-	if (sm->diag.ptr < 0)
-		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
-			sm->diag.ptr = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
-{
-	int val;
-
-	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
-	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
-	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
-		return;
-	val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
-	/* clip */
-	if (val > SHRT_MAX)
-		val = SHRT_MAX;
-	if (val < SHRT_MIN)
-		val = SHRT_MIN;
-	sm->diag.data[sm->diag.ptr++] = val;
-}
-
-/* --------------------------------------------------------------------- */
-
-extern inline void diag_add_one(struct sm_state *sm, int val)
-{
-	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
-	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
-	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
-		return;
-	/* clip */
-	if (val > SHRT_MAX)
-		val = SHRT_MAX;
-	if (val < SHRT_MIN)
-		val = SHRT_MIN;
-	sm->diag.data[sm->diag.ptr++] = val;
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
-{
-	if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
-	    sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
-		return;
-	/* clip */
-	if (vali > SHRT_MAX)
-		vali = SHRT_MAX;
-	if (vali < SHRT_MIN)
-		vali = SHRT_MIN;
-	if (valq > SHRT_MAX)
-		valq = SHRT_MAX;
-	if (valq < SHRT_MIN)
-		valq = SHRT_MIN;
-	sm->diag.data[sm->diag.ptr++] = vali;
-	sm->diag.data[sm->diag.ptr++] = valq;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== utility functions ===============================
- */
-
-extern inline unsigned int hweight32(unsigned int w)
-	__attribute__ ((unused));
-extern inline unsigned int hweight16(unsigned short w)
-	__attribute__ ((unused));
-extern inline unsigned int hweight8(unsigned char w)
-        __attribute__ ((unused));
-
-extern inline unsigned int hweight32(unsigned int w)
-{
-        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
-        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
-        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
-        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
-        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
-}
-
-extern inline unsigned int hweight16(unsigned short w)
-{
-        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
-        res = (res & 0x3333) + ((res >> 2) & 0x3333);
-        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
-        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
-}
-
-extern inline unsigned int hweight8(unsigned char w)
-{
-        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
-        res = (res & 0x33) + ((res >> 2) & 0x33);
-        return (res & 0x0F) + ((res >> 4) & 0x0F);
-}
-
-extern inline unsigned int gcd(unsigned int x, unsigned int y)
-	__attribute__ ((unused));
-extern inline unsigned int lcm(unsigned int x, unsigned int y)
-	__attribute__ ((unused));
-
-extern inline unsigned int gcd(unsigned int x, unsigned int y)
-{
-	for (;;) {
-		if (!x)
-			return y;
-		if (!y)
-			return x;
-		if (x > y)
-			x %= y;
-		else
-			y %= x;
-	}
-}
-
-extern inline unsigned int lcm(unsigned int x, unsigned int y)
-{
-	return x * y / gcd(x, y);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== profiling =======================================
- */
-
-
-#if defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686))
-
-/*
- * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
- * in fact, overflowing modems would require over 2THz clock speeds :-)
- */
-
-#define time_exec(var,cmd)                                      \
-({                                                              \
-	unsigned int cnt1, cnt2, cnt3;                          \
-	__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3));  \
-	cmd;                                                    \
-	__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3));  \
-	var = cnt2-cnt1;                                        \
-})
-#else /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
-
-#define time_exec(var,cmd) cmd
-
-#endif /* defined(SM_DEBUG) && (defined(CONFIG_M586) || defined(CONFIG_M686)) */
-
-/* --------------------------------------------------------------------- */
-
-#ifdef CONFIG_SOUNDMODEM_WSS
-#include "sm_wss.h"
-#endif /* CONFIG_SOUNDMODEM_WSS */
-#ifdef CONFIG_SOUNDMODEM_SBC
-#include "sm_sbc.h"
-#endif /* CONFIG_SOUNDMODEM_SBC */
-
-#ifdef CONFIG_SOUNDMODEM_AFSK1200
-#include "sm_afsk1200.h"
-#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
-#ifdef CONFIG_SOUNDMODEM_FSK9600
-#include "sm_fsk9600.h"
-#endif /* CONFIG_SOUNDMODEM_FSK9600 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2666
-#include "sm_afsk2666.h"
-#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
-#ifdef CONFIG_SOUNDMODEM_PSK4800
-#include "sm_psk4800.h"
-#endif /* CONFIG_SOUNDMODEM_PSK4800 */
-
-/* --------------------------------------------------------------------- */
-
-static int sm_open(struct device *dev);
-static int sm_close(struct device *dev);
-static int sm_ioctl(struct device *dev, struct ifreq *ifr,
-		    struct hdlcdrv_ioctl *hi, int cmd);
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info *modem_tx_base = NEXT_TX_INFO;
-const struct modem_rx_info *modem_rx_base = NEXT_RX_INFO;
-const struct hardware_info *hardware_base = NEXT_HW_INFO;
-
-static const struct hdlcdrv_ops sm_ops = {
-	sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
-};
-
-/* --------------------------------------------------------------------- */
-
-static int sm_open(struct device *dev)
-{
-	struct sm_state *sm;
-	int err;
-
-	if (!dev || !dev->priv ||
-	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "sm_open: invalid device struct\n");
-		return -EINVAL;
-	}
-	sm = (struct sm_state *)dev->priv;
-
-	if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
-		return -ENODEV;
-	sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
-	err = sm->hwdrv->open(dev, sm);
-	if (err)
-		return err;
-	output_open(sm);
-	MOD_INC_USE_COUNT;
-	printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u\n",
-	       sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
-	       sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_close(struct device *dev)
-{
-	struct sm_state *sm;
-	int err = -ENODEV;
-
-	if (!dev || !dev->priv ||
-	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "sm_close: invalid device struct\n");
-		return -EINVAL;
-	}
-	sm = (struct sm_state *)dev->priv;
-
-
-	if (sm->hwdrv && sm->hwdrv->close)
-		err = sm->hwdrv && sm->hwdrv->close(dev, sm);
-	output_close(sm);
-	MOD_DEC_USE_COUNT;
-	printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
-	       sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
-	return err;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, ':');
-	const struct hardware_info *hwp = hardware_base;
-
-	if (!cp)
-		cp = mode;
-	else {
-		*cp++ = '\0';
-		while (hwp && hwp->hw_name && strcmp(hwp->hw_name, mode))
-			hwp = hwp->next;
-		if (!hwp || !hwp->hw_name)
-			return -EINVAL;
-		if (hwp->loc_storage > sizeof(sm->hw)) {
-			printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
-			       sm_drvname, hwp->hw_name, hwp->loc_storage);
-			return -EINVAL;
-		}
-		sm->hwdrv = hwp;
-	}
-	if (!*cp)
-		return 0;
-	if (sm->hwdrv && sm->hwdrv->sethw)
-		return sm->hwdrv->sethw(dev, sm, cp);
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_ioctl(struct device *dev, struct ifreq *ifr,
-		    struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct sm_state *sm;
-	struct sm_ioctl bi;
-	unsigned long flags;
-	unsigned int newdiagmode;
-	unsigned int newdiagflags;
-	char *cp;
-	const struct modem_tx_info *mtp = modem_tx_base;
-	const struct modem_rx_info *mrp = modem_rx_base;
-	const struct hardware_info *hwp = hardware_base;
-
-	if (!dev || !dev->priv ||
-	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "sm_ioctl: invalid device struct\n");
-		return -EINVAL;
-	}
-	sm = (struct sm_state *)dev->priv;
-
-	if (cmd != SIOCDEVPRIVATE) {
-		if (!sm->hwdrv || !sm->hwdrv->ioctl)
-			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
-		return -ENOIOCTLCMD;
-	}
-	switch (hi->cmd) {
-	default:
-		if (sm->hwdrv && sm->hwdrv->ioctl)
-			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
-		return -ENOIOCTLCMD;
-
-	case HDLCDRVCTL_GETMODE:
-		cp = hi->data.modename;
-		if (sm->hwdrv && sm->hwdrv->hw_name)
-			cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
-		else
-			cp += sprintf(cp, "<unspec>:");
-		if (sm->mode_tx && sm->mode_tx->name)
-			cp += sprintf(cp, "%s", sm->mode_tx->name);
-		else
-			cp += sprintf(cp, "<unspec>");
-		if (!sm->mode_rx || !sm->mode_rx ||
-		    strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
-			if (sm->mode_rx && sm->mode_rx->name)
-				cp += sprintf(cp, ",%s", sm->mode_rx->name);
-			else
-				cp += sprintf(cp, ",<unspec>");
-		}
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_SETMODE:
-		if (!suser() || dev->start)
-			return -EACCES;
-		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
-		return sethw(dev, sm, hi->data.modename);
-
-	case HDLCDRVCTL_MODELIST:
-		cp = hi->data.modename;
-		while (hwp) {
-			if (hwp->hw_name)
-				cp += sprintf("%s:,", hwp->hw_name);
-			hwp = hwp->next;
-		}
-		while (mtp) {
-			if (mtp->name)
-				cp += sprintf(">%s,", mtp->name);
-			mtp = mtp->next;
-		}
-		while (mrp) {
-			if (mrp->name)
-				cp += sprintf("<%s,", mrp->name);
-			mrp = mrp->next;
-		}
-		cp[-1] = '\0';
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
-			return -EFAULT;
-		return 0;
-
-#ifdef SM_DEBUG
-	case SMCTL_GETDEBUG:
-		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-			return -EFAULT;
-		bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
-		bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
-		bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
-		bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
-		sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
-			sm->debug_vals.dma_residue = 0;
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return 0;
-#endif /* SM_DEBUG */
-
-	case SMCTL_DIAGNOSE:
-		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-			return -EFAULT;
-		newdiagmode = bi.data.diag.mode;
-		newdiagflags = bi.data.diag.flags;
-		if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
-			return -EINVAL;
-		bi.data.diag.mode = sm->diag.mode;
-		bi.data.diag.flags = sm->diag.flags;
-		bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
-		if (sm->diag.mode != newdiagmode) {
-			save_flags(flags);
-			cli();
-			sm->diag.ptr = -1;
-			sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
-			sm->diag.mode = newdiagmode;
-			restore_flags(flags);
-			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-				return -EFAULT;
-			return 0;
-		}
-		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
-			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-				return -EFAULT;
-			return 0;
-		}
-		if (bi.data.diag.datalen > DIAGDATALEN)
-			bi.data.diag.datalen = DIAGDATALEN;
-		if (sm->diag.ptr < bi.data.diag.datalen) {
-			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-				return -EFAULT;
-			return 0;
-		}
-		if (copy_to_user(bi.data.diag.data, sm->diag.data,
-				 bi.data.diag.datalen * sizeof(short)))
-			return -EFAULT;
-		bi.data.diag.flags |= SM_DIAGFLAG_VALID;
-		save_flags(flags);
-		cli();
-		sm->diag.ptr = -1;
-		sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
-		sm->diag.mode = newdiagmode;
-		restore_flags(flags);
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return 0;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-static
-#endif /* MODULE */
-int sm_init(void)
-{
-	int i, j, found = 0;
-	char set_hw = 1;
-	struct sm_state *sm;
-	char ifname[HDLCDRV_IFNAMELEN];
-
-	printk(sm_drvinfo);
-	/*
-	 * register net devices
-	 */
-	for (i = 0; i < NR_PORTS; i++) {
-		struct device *dev = sm_device+i;
-		sprintf(ifname, "sm%d", i);
-
-		if (!sm_ports[i].mode)
-			set_hw = 0;
-		if (!set_hw)
-			sm_ports[i].iobase = sm_ports[i].irq = 0;
-		j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state),
-					     ifname, sm_ports[i].iobase,
-					     sm_ports[i].irq, sm_ports[i].dma);
-		if (!j) {
-			sm = (struct sm_state *)dev->priv;
-			sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2;
-			sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
-			sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
-			sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
-			if (set_hw && sethw(dev, sm, sm_ports[i].mode))
-				set_hw = 0;
-			found++;
-		} else {
-			printk(KERN_WARNING "%s: cannot register net device\n",
-			       sm_drvname);
-		}
-	}
-	if (!found)
-		return -ENXIO;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-/*
- * command line settable parameters
- */
-char *mode = NULL;
-int iobase = -1;
-int irq = -1;
-int dma = -1;
-int dma2 = -1;
-int serio = 0;
-int pario = 0;
-int midiio = 0;
-
-MODULE_PARM(mode, "s");
-MODULE_PARM(iobase, "i");
-MODULE_PARM(irq, "i");
-MODULE_PARM(dma, "i");
-MODULE_PARM(dma2, "i");
-MODULE_PARM(serio, "i");
-MODULE_PARM(pario, "i");
-MODULE_PARM(midiio, "i");
-
-int init_module(void)
-{
-	if (mode) {
-		if (iobase == -1)
-			iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530;
-		if (irq == -1)
-			irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11;
-		if (dma == -1)
-			dma = 1;
-	}
-	sm_ports[0].mode = mode;
-	sm_ports[0].iobase = iobase;
-	sm_ports[0].irq = irq;
-	sm_ports[0].dma = dma;
-	sm_ports[0].dma2 = dma2;
-	sm_ports[0].seriobase = serio;
-	sm_ports[0].pariobase = pario;
-	sm_ports[0].midiiobase = midiio;
-	sm_ports[1].mode = NULL;
-
-	return sm_init();
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	int i;
-
-	printk(KERN_INFO "sm: cleanup_module called\n");
-
-	for(i = 0; i < NR_PORTS; i++) {
-		struct device *dev = sm_device+i;
-		struct sm_state *sm = (struct sm_state *)dev->priv;
-
-		if (sm) {
-			if (sm->hdrv.magic != HDLCDRV_MAGIC)
-				printk(KERN_ERR "sm: invalid magic in "
-				       "cleanup_module\n");
-			else
-				hdlcdrv_unregister_hdlcdrv(dev);
-		}
-	}
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode
- * mode: hw:modem
- * hw: sbc, wss, wssfdx
- * modem: afsk1200, fsk9600
- */
-
-void sm_setup(char *str, int *ints)
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++);
-	if ((i >= NR_PORTS) || (ints[0] < 4)) {
-		printk(KERN_INFO "%s: too many or invalid interface "
-		       "specifications\n", sm_drvname);
-		return;
-	}
-	sm_ports[i].mode = str;
-	sm_ports[i].iobase = ints[1];
-	sm_ports[i].irq = ints[2];
-	sm_ports[i].dma = ints[3];
-	sm_ports[i].dma2 = (ints[0] >= 5) ? ints[4] : 0;
-	sm_ports[i].seriobase = (ints[0] >= 6) ? ints[5] : 0;
-	sm_ports[i].pariobase = (ints[0] >= 7) ? ints[6] : 0;
-	sm_ports[i].midiiobase = (ints[0] >= 8) ? ints[7] : 0;
-	if (i < NR_PORTS-1)
-		sm_ports[i+1].mode = NULL;
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.26/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v2.1.26/linux/drivers/net/strip.c	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/strip.c	Fri Feb 21 14:58:35 1997
@@ -1621,7 +1621,7 @@
      */
     if (haddr.c[0] == 0xFF)
     {
-	    memcpy(haddr.c, dev->broadcast, sizeof(haddr));
+	    memcpy(haddr.c, strip_info->dev.broadcast, sizeof(haddr));
 	    if (haddr.c[0] == 0xFF)
 	    {
 		    strip_info->tx_dropped++;
diff -u --recursive --new-file v2.1.26/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.26/linux/drivers/pci/pci.c	Fri Feb  7 04:36:07 1997
+++ linux/drivers/pci/pci.c	Fri Feb 21 14:58:35 1997
@@ -256,6 +256,7 @@
 	DEVICE( INTEL,		INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE( INTEL,		INTEL_82437VX,	"82437VX Triton II"),
 	DEVICE( INTEL,		INTEL_P6,	"Orion P6"),
+	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
 	DEVICE( ADAPTEC,	ADAPTEC_7860,	"AIC-7860"),
@@ -546,6 +547,7 @@
 	      case PCI_VENDOR_ID_AVANCE:	return "Avance";
 	      case PCI_VENDOR_ID_S3:		return "S3 Inc.";
 	      case PCI_VENDOR_ID_INTEL:		return "Intel";
+	      case PCI_VENDOR_ID_KTI:		return "KTI";
 	      case PCI_VENDOR_ID_ADAPTEC:	return "Adaptec";
 	      case PCI_VENDOR_ID_ATRONICS:	return "Atronics";
 	      case PCI_VENDOR_ID_ARK:		return "ARK Logic";
diff -u --recursive --new-file v2.1.26/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v2.1.26/linux/drivers/scsi/eata.c	Tue Jan 14 02:48:50 1997
+++ linux/drivers/scsi/eata.c	Wed Feb 26 10:56:28 1997
@@ -1,6 +1,25 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ *          When loading as a module, parameter passing is now supported
+ *          both in 2.0 and in 2.1 style.
+ *          Fixed data transfer direction for some SCSI opcodes.
+ *          Immediate acknowledge to request sense commands.
+ *          Linked commands to each disk device are now reordered by elevator
+ *          sorting. Rare cases in which reordering of write requests could 
+ *          cause wrong results are managed.
+ *          Fixed spurious timeouts caused by long simple queue tag sequences.
+ *          New command line option (tm:[0-3]) to choose the type of tags:
+ *          0 -> mixed (default); 1 -> simple; 2 -> head; 3 -> ordered.
+ *
+ *      18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ *          Added command line options to enable/disable linked commands
+ *          (lc:[y|n]), tagged commands (tc:[y|n]) and to set the max queue
+ *          depth (mq:xx). Default is "eata=lc:n,tc:n,mq:16".
+ *          Improved command linking.
+ *          Documented how to setup RAID-0 with DPT SmartRAID boards.
+ *
  *       8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
  *          Added linked command support.
  *          Improved detection of PCI boards using ISA base addresses.
@@ -12,7 +31,7 @@
  *          When CONFIG_PCI is defined, BIOS32 is used to include in the
  *          list of i/o ports to be probed all the PCI SCSI controllers.
  *          The list of i/o ports to be probed can be overwritten by the
- *          "eata=port0, port1,...." boot command line option.
+ *          "eata=port0,port1,...." boot command line option.
  *          Scatter/gather lists are now allocated by a number of kmalloc
  *          calls, in order to avoid the previous size limit of 64Kb.
  *
@@ -129,13 +148,16 @@
  *  v003.D0, firmware v07G.0).
  *
  *  DPT SmartRAID boards support "Hardware Array" - a group of disk drives
- *  which are all members of the same RAID-1 or RAID-5 array implemented
+ *  which are all members of the same RAID-0, RAID-1 or RAID-5 array implemented
  *  in host adapter hardware. Hardware Arrays are fully compatible with this
  *  driver, since they look to it as a single disk drive.
- *  By contrast RAID-0 are implemented as "Array Group", which does not
- *  seem to be supported correctly by the actual SCSI subsystem.
- *  To get RAID-0 functionality, the linux MD driver must be used instead of
- *  the DPT "Array Group" feature.
+ *
+ *  WARNING: to create a RAID-0 "Hardware Array" you must select "Other Unix"
+ *  as the current OS in the DPTMGR "Initial System Installation" menu.
+ *  Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0), 
+ *  which is not supported by the actual SCSI subsystem.
+ *  To get the "Array Group" functionality, the Linux MD driver must be used
+ *  instead of the DPT "Array Group" feature.
  *
  *  Multiple ISA, EISA and PCI boards can be configured in the same system.
  *  It is suggested to put all the EISA boards on the same IRQ level, all
@@ -167,21 +189,86 @@
  *  - ISA  0x170, 0x230, 0x330.
  * 
  *  The above list of detection probes can be totally replaced by the
- *  boot command line option: "eata=port0, port1, port2,...", where the
+ *  boot command line option: "eata=port0,port1,port2,...", where the
  *  port0, port1... arguments are ISA/EISA/PCI addresses to be probed.
- *  For example using "eata=0x7410, 0x7450, 0x230", the driver probes
+ *  For example using "eata=0x7410,0x7450,0x230", the driver probes
  *  only the two PCI addresses 0x7410 and 0x7450 and the ISA address 0x230,
  *  in this order; "eata=0" totally disables this driver.
  *
+ *  After the optional list of detection probes, other possible command line
+ *  options are:
+ *
+ *  lc:y  enables linked commands;
+ *  lc:n  disables linked commands;
+ *  tc:y  enables tagged commands;
+ *  tc:n  disables tagged commands;
+ *  tm:0  use head/simple/ordered queue tag sequences for reads and ordered
+ *        queue tags for writes;
+ *  tm:1  use only simple queue tags;
+ *  tm:2  use only head of queue tags;
+ *  tm:3  use only ordered queue tags;
+ *  mq:xx set the max queue depth to the value xx (2 <= xx <= 32).
+ *
+ *  The default value is: "eata=lc:n,tc:n,mq:16,tm:0". An example using
+ *  the list of detection probes could be: "eata=0x7410,0x230,lc:y,tc:n,mq:4".
+ *
+ *  When loading as a module, parameters can be specified as well.
+ *  The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ *  modprobe eata io_port=0x7410,0x230 linked_comm=1 tagged_comm=0 \
+ *                max_queue_depth=4 tag_mode=0
+ *
+ *  ----------------------------------------------------------------------------
+ *  In this implementation, linked commands are designed to work with any DISK
+ *  or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ *  and reordering by elevator sorting commands directed to each device,
+ *  without any relation with the actual SCSI protocol between the controller
+ *  and the device.
+ *  If Q is the queue depth reported at boot time for each device (also named
+ *  cmds/lun) and Q > 2, whenever there is already an active command to the
+ *  device all other commands to the same device  (up to Q-1) are kept waiting
+ *  in the elevator sorting queue. When the active command completes, the
+ *  commands in this queue are sorted by sector address. The sort is chosen
+ *  between increasing or decreasing by minimizing the seek distance between
+ *  the sector of the commands just completed and the sector of the first 
+ *  command in the list to be sorted. 
+ *  Trivial math assures that if there are (Q-1) outstanding request for
+ *  random seeks over S sectors, the unsorted average seek distance is S/2,
+ *  while the sorted average seek distance is S/(Q-1). The seek distance is
+ *  hence divided by a factor (Q-1)/2.
+ *  The above pure geometric remarks are valid in all cases and the 
+ *  driver effectively reduces the seek distance by the predicted factor
+ *  when there are Q concurrent read i/o operations on the device, but this
+ *  does not necessarily results in a noticeable performance improvement:
+ *  your mileage may vary....
+ *
+ *  Note: command reordering inside a batch of queued commands could cause
+ *        wrong results only if there is at least one write request and the
+ *        intersection (sector-wise) of all requests is not empty. 
+ *        When the driver detects a batch including overlapping requests
+ *        (a really rare event) strict serial (pid) order is enforced.
+ *  ----------------------------------------------------------------------------
+ *
  *  The boards are named EATA0, EATA1,... according to the detection order.
  *
  *  In order to support multiple ISA boards in a reliable way,
  *  the driver sets host->wish_block = TRUE for all ISA boards.
  */
 
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define MAX_INT_PARAM 10
 #if defined(MODULE)
 #include <linux/module.h>
 #include <linux/version.h>
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
+MODULE_PARM(linked_comm, "i");
+MODULE_PARM(tagged_comm, "i");
+MODULE_PARM(link_statistics, "i");
+MODULE_PARM(max_queue_depth, "i");
+MODULE_PARM(tag_mode, "i");
+MODULE_AUTHOR("Dario Ballabio");
+#endif
 #endif
 
 #include <linux/string.h>
@@ -235,9 +322,9 @@
 #define MAX_LARGE_SGLIST 252
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
-#define MAX_TAGGED_CMD_PER_LUN 16
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
 
-#define SKIP 1
+#define SKIP UINT_MAX
 #define FALSE 0
 #define TRUE 1
 #define FREE 0
@@ -249,6 +336,10 @@
 #define ABORTING 6
 #define NO_DMA  0xff
 #define MAXLOOP 200000
+#define TAG_MIXED    0
+#define TAG_SIMPLE   1
+#define TAG_HEAD     2
+#define TAG_ORDERED  3
 
 #define REG_CMD         7
 #define REG_STATUS      7
@@ -281,6 +372,8 @@
 #define ASST              0x01
 
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
 
 /* "EATA", in Big Endian format */
 #define EATA_SIGNATURE 0x41544145
@@ -298,7 +391,8 @@
 	  version:4;    /* EATA version, should be 0x1 */
    unchar  ocsena:1,    /* Overlap Command Support Enabled */
 	   tarsup:1,    /* Target Mode Supported */
-		 :2,
+           trnxfr:1,    /* Truncate Transfer Cmd NOT Necessary */
+           morsup:1,    /* More Supported */
 	   dmasup:1,    /* DMA Supported */
 	   drqvld:1,    /* DRQ Index (DRQX) is valid */
 	      ata:1,    /* This is an ATA device */
@@ -327,10 +421,13 @@
 
    /* Structure extension defined in EATA 2.0C */
    unchar   max_lun;    /* Max SCSI LUN number */
-   unchar        :6,
+   unchar        :4,
+               m1:1,    /* This is a PCI with an M1 chip installed */
+          idquest:1,    /* RAIDNUM returned is questionable */
               pci:1,    /* This board is PCI */
              eisa:1;    /* This board is EISA */
-   unchar   notused[2];
+   unchar   raidnum;    /* Uniquely identifies this HBA in a system */
+   unchar   notused;
 
    ushort ipad[247];
    };
@@ -373,9 +470,13 @@
 	     dout:1,     /* Direction of Transfer is Out (Host to Target) */
 	      din:1;     /* Direction of Transfer is In (Target to Host) */
    unchar sense_len;     /* Request Sense Length */
-   unchar unused[4];
+   unchar unused[3];
+   unchar  fwnest:1,     /* Send command to a component of an Array Group */
+                 :7;
    unchar phsunit:1,     /* Send to Target Physical Unit (bypass RAID) */
-	  notused:7;
+              iat:1,     /* Inhibit Address Translation */
+            hbaci:1,     /* Inhibit HBA Caching for this command */
+                 :5;
    unchar  target:5,     /* SCSI target ID */
           channel:3;     /* SCSI channel number */
    unchar     lun:5,     /* SCSI logical unit number */
@@ -390,7 +491,6 @@
    ulong  sp_addr;       /* Address where sp is DMA'ed when cp completes */
    ulong  sense_addr;    /* Address where Sense Data is DMA'ed on error */
    unsigned int index;   /* cp index */
-   unsigned int link_id; /* reference cp for linked commands */
    struct sg_list *sglist;
    };
 
@@ -417,14 +517,18 @@
 static const char *driver_name = "EATA";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[MAX_BOARDS + 1] = { 
+static unsigned int io_port[] = { 
+
+   /* Space for MAX_INT_PARAM ports usable while loading as a module */
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,
 
    /* First ISA */
    0x1f0,
 
    /* Space for MAX_PCI ports possibly reported by PCI_BIOS */
-    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
-    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
 
    /* MAX_EISA ports */
    0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
@@ -446,19 +550,22 @@
 #define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0)
 
 static void eata2x_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int);
 static int do_trace = FALSE;
 static int setup_done = FALSE;
+static int link_statistics = 0;
+static int tag_mode = TAG_MIXED;
 
 #if defined (CONFIG_SCSI_EATA_TAGGED_QUEUE)
-static int tagged_commands = TRUE;
+static int tagged_comm = TRUE;
 #else
-static int tagged_commands = FALSE;
+static int tagged_comm = FALSE;
 #endif
 
 #if defined (CONFIG_SCSI_EATA_LINKED_COMMANDS)
-static int linked_commands = TRUE;
+static int linked_comm = TRUE;
 #else
-static int linked_commands = FALSE;
+static int linked_comm = FALSE;
 #endif
 
 #if defined CONFIG_SCSI_EATA_MAX_TAGS
@@ -481,37 +588,50 @@
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) ntag++;
-      else                       nuntag++;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         ntag++;
+      else
+         nuntag++;
       }
 
    utqd = MAX_CMD_PER_LUN;
 
-   tqd = (host->can_queue - utqd * nuntag) / (ntag + 1);
+   tqd = (host->can_queue - utqd * nuntag) / (ntag ? ntag : 1);
 
    if (tqd > max_queue_depth) tqd = max_queue_depth;
 
    if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
 
    for(dev = devlist; dev; dev = dev->next) {
-      char *tag_suffix = "";
+      char *tag_suffix = "", *link_suffix = "";
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) dev->queue_depth = tqd;
-      else                       dev->queue_depth = utqd;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         dev->queue_depth = tqd;
+      else
+         dev->queue_depth = utqd;
 
-      if (tagged_commands && dev->tagged_supported) {
+      if (TLDEV(dev->type)) {
+         if (linked_comm && dev->queue_depth > 2)
+            link_suffix = ", linked";
+         else
+            link_suffix = ", unlinked";
+         }
+
+      if (tagged_comm && dev->tagged_supported && TLDEV(dev->type)) {
          dev->tagged_queue = 1;
          dev->current_tag = 1;
          }
 
-      if (dev->tagged_supported && dev->tagged_queue) tag_suffix = ", tagged";
-      else if (dev->tagged_supported) tag_suffix = ", untagged";
+      if (dev->tagged_supported && TLDEV(dev->type) && dev->tagged_queue)
+         tag_suffix = ", tagged";
+      else if (dev->tagged_supported && TLDEV(dev->type))
+         tag_suffix = ", untagged";
 
-      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s.\n",
+      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
              BN(j), host->host_no, dev->channel, dev->id, dev->lun,
-             dev->queue_depth, tag_suffix);
+             dev->queue_depth, link_suffix, tag_suffix);
       }
 
    restore_flags(flags);
@@ -563,7 +683,7 @@
    unsigned char irq, dma_channel, subversion, i;
    unsigned char protocol_rev;
    struct eata_info info;
-   char *bus_type, dma_name[16];
+   char *bus_type, dma_name[16], tag_type;
 
    /* Allowed DMA channels for ISA (0 indicates reserved) */
    unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
@@ -779,10 +899,18 @@
 
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
 
-   printk("%s: 2.0%c, %s 0x%03x, IRQ %u, %s, SG %d, MB %d, TC %d, LC %d, "\
-          "MQ %d.\n", BN(j), HD(j)->protocol_rev, bus_type, sh[j]->io_port,
+   if (tagged_comm) {
+      if      (tag_mode == TAG_SIMPLE)  tag_type = '1';
+      else if (tag_mode == TAG_HEAD)    tag_type = '2';
+      else if (tag_mode == TAG_ORDERED) tag_type = '3';
+      else                              tag_type = 'y';
+      }
+   else                                 tag_type = 'n';
+
+   printk("%s: 2.0%c, %s 0x%03x, IRQ %u, %s, SG %d, MB %d, tc:%c, lc:%c, "\
+          "mq:%d.\n", BN(j), HD(j)->protocol_rev, bus_type, sh[j]->io_port,
           sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
-          tagged_commands, linked_commands, max_queue_depth);
+          tag_type, YESNO(linked_comm), max_queue_depth);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -793,10 +921,11 @@
              BN(j), i, info.host_addr[3 - i]);
 
 #if defined (DEBUG_DETECT)
-   printk("%s: Vers. 0x%x, ocs %u, tar %u, SYNC 0x%x, sec. %u, "\
-          "infol %ld, cpl %ld spl %ld.\n", name, info.version,
-          info.ocsena, info.tarsup, info.sync, info.second,
-          DEV2H(info.data_len), DEV2H(info.cp_len), DEV2H(info.sp_len));
+   printk("%s: Vers. 0x%x, ocs %u, tar %u, trnxfr %u, more %u, SYNC 0x%x, "\
+          "sec. %u, infol %ld, cpl %ld spl %ld.\n", name, info.version,
+          info.ocsena, info.tarsup, info.trnxfr, info.morsup, info.sync,
+          info.second, DEV2H(info.data_len), DEV2H(info.cp_len),
+          DEV2H(info.sp_len));
 
    if (protocol_rev == 'B' || protocol_rev == 'C')
       printk("%s: isaena %u, forcaddr %u, max_id %u, max_chan %u, "\
@@ -804,8 +933,9 @@
              info.max_id, info.max_chan, info.large_sg, info.res1);
 
    if (protocol_rev == 'C')
-      printk("%s: max_lun %u, pci %u, eisa %u.\n", name, 
-             info.max_lun, info.pci, info.eisa);
+      printk("%s: max_lun %u, m1 %u, idquest %u, pci %u, eisa %u, "\
+             "raidnum %u.\n", name, info.max_lun, info.m1, info.idquest, 
+             info.pci, info.eisa, info.raidnum);
 #endif
 
    return TRUE;
@@ -813,15 +943,34 @@
 
 void eata2x_setup(char *str, int *ints) {
    int i, argc = ints[0];
+   char *cur = str, *pc;
 
-   if (argc <= 0) return;
+   if (argc > 0) {
 
-   if (argc > MAX_BOARDS) argc = MAX_BOARDS;
+      if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
 
-   for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
+      for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
    
-   io_port[i] = 0;
-   setup_done = TRUE;
+      io_port[i] = 0;
+      setup_done = TRUE;
+      }
+
+   while (cur && (pc = strchr(cur, ':'))) {
+      int val = 0, c = *++pc;
+
+      if (c == 'n' || c == 'N') val = FALSE;
+      else if (c == 'y' || c == 'Y') val = TRUE;
+      else val = (int) simple_strtoul(pc, NULL, 0);
+
+      if (!strncmp(cur, "lc:", 3)) linked_comm = val;
+      else if (!strncmp(cur, "tc:", 3)) tagged_comm = val;
+      else if (!strncmp(cur, "tm:", 3)) tag_mode = val;
+      else if (!strncmp(cur, "mq:", 3))  max_queue_depth = val;
+      else if (!strncmp(cur, "ls:", 3))  link_statistics = val;
+
+      if ((cur = strchr(cur, ','))) ++cur;
+      }
+
    return;
 }
 
@@ -852,7 +1001,7 @@
              continue;
 
       /* Reverse the returned address order */
-      io_port[MAX_PCI - k] = 
+      io_port[MAX_INT_PARAM + MAX_PCI - k] = 
              (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
       }
 #endif
@@ -869,6 +1018,14 @@
    save_flags(flags);
    cli();
 
+#if defined(MODULE)
+   /* io_port could have been modified when loading as a module */
+   if(io_port[0] != SKIP) {
+      setup_done = TRUE;
+      io_port[MAX_INT_PARAM] = 0;
+      }
+#endif
+
    for (k = 0; k < MAX_IRQ; k++) {
       irqlist[k] = 0;
       calls[k] = 0;
@@ -914,9 +1071,15 @@
    struct mssp *spp;
 
    static const unsigned char data_out_cmds[] = {
-      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x0b, 0x10, 0x16, 0x18, 0x1d, 
-      0x24, 0x2b, 0x2e, 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 
-      0x3f, 0x40, 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea
+      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+      0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40, 
+      0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b
+      };
+
+   static const unsigned char data_none_cmds[] = {
+      0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+      0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+      0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5
       };
 
    save_flags(flags);
@@ -926,6 +1089,16 @@
 
    if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
 
+   if (SCpnt->cmnd[0] == REQUEST_SENSE && SCpnt->sense_buffer[0]) {
+      SCpnt->result = DID_OK << 16; 
+      SCpnt->host_scribble = NULL;
+      printk("%s: qcomm, target %d.%d:%d, pid %ld, request sense ignored.\n",
+             BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid);
+      restore_flags(flags);
+      done(SCpnt);    
+      return 0;
+      }
+
    /* i is the mailbox number, look for the first free mailbox 
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
@@ -984,7 +1157,13 @@
 	break;
 	}
 
-   cpp->din = !cpp->dout;
+   if ((cpp->din = !cpp->dout))
+      for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+        if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+	   cpp->din = FALSE;
+	   break;
+	   }
+
    cpp->reqsen = TRUE;
    cpp->dispri = TRUE;
    cpp->one = TRUE;
@@ -997,11 +1176,20 @@
    
    if (SCpnt->device->tagged_queue) {
 
-      if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] || 
-                            HD(j)->target_to[SCpnt->target][SCpnt->channel])
+      if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] ||
+            HD(j)->target_to[SCpnt->target][SCpnt->channel])
          cpp->mess[0] = ORDERED_QUEUE_TAG;
-      else
+      else if (tag_mode == TAG_SIMPLE)  cpp->mess[0] = SIMPLE_QUEUE_TAG;
+      else if (tag_mode == TAG_HEAD)    cpp->mess[0] = HEAD_OF_QUEUE_TAG;
+      else if (tag_mode == TAG_ORDERED) cpp->mess[0] = ORDERED_QUEUE_TAG;
+      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 0)
+         cpp->mess[0] = ORDERED_QUEUE_TAG;
+      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 1)
+         cpp->mess[0] = HEAD_OF_QUEUE_TAG;
+      else if (cpp->din)
          cpp->mess[0] = SIMPLE_QUEUE_TAG;
+      else
+         cpp->mess[0] = ORDERED_QUEUE_TAG;
 
       cpp->mess[1] = SCpnt->device->current_tag++;
       }
@@ -1017,27 +1205,13 @@
 
    memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
 
-   if (linked_commands && SCpnt->device->tagged_supported
-            && !HD(j)->target_to[SCpnt->target][SCpnt->channel]
-	    && !HD(j)->target_redo[SCpnt->target][SCpnt->channel])
-   
-      for (k = 0; k < sh[j]->can_queue; k++) {
- 
-         if (HD(j)->cp_stat[k] != IN_USE) continue;
-
-         if ((&HD(j)->cp[k])->SCpnt->device != SCpnt->device) continue;
-
-         cpp->link_id = k;
-         HD(j)->cp_stat[i] = READY;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-         printk("%s: qcomm, target %d.%d:%d, pid %ld, Mbox %d ready.\n", 
-                BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun,
-                SCpnt->pid, i);
-#endif
-         restore_flags(flags);
-         return 0;
-         }
+   if (linked_comm && SCpnt->device->queue_depth > 2
+                                     && TLDEV(SCpnt->device->type)) {
+      HD(j)->cp_stat[i] = READY;
+      flush_dev(SCpnt->device, 0, j, FALSE);
+      restore_flags(flags);
+      return 0;
+      }
 
    /* Send control packet to the board */
    if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
@@ -1273,38 +1447,152 @@
       }
 }
 
-static inline void process_ready_list(unsigned int i, unsigned int j) {
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+                 unsigned int rev) {
+   unsigned int i, j, k, y;
+   unsigned long x;
+
+   for (i = 0; i < n - 1; i++) {
+      k = i;
+
+      for (j = k + 1; j < n; j++) 
+         if (rev) {
+            if (sk[j] > sk[k]) k = j;
+            }
+         else {
+            if (sk[j] < sk[k]) k = j;
+            }
+
+      if (k != i) {
+         x = sk[k]; sk[k] = sk[i]; sk[i] = x;
+         y = da[k]; da[k] = da[i]; da[i] = y;
+         }
+      }
+
+   return;
+   }
+
+static inline void reorder(unsigned int j, unsigned long cursec,
+                 unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
    Scsi_Cmnd *SCpnt;
-   unsigned int k, n_ready = 0;
    struct mscp *cpp;
+   unsigned int k, n;
+   unsigned int rev = FALSE, s = TRUE, r = TRUE;
+   unsigned int input_only = TRUE, overlap = FALSE;
+   unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+
+   static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+   static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+   static unsigned int readysorted = 0, revcount = 0;
+   static unsigned long seeksorted = 0, seeknosort = 0;
+
+   if (link_statistics && !(++flushcount % link_statistics))
+      printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
+             " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
+             ovlcount, readycount, readysorted, sortcount, revcount,
+             seeknosort / (readycount - batchcount + 1), 
+             seeksorted / (readycount - batchcount + 1));
+
+   if (n_ready <= 1) return;
+
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+      if (!cpp->din) input_only = FALSE;
+
+      if (SCpnt->request.sector < minsec) minsec = SCpnt->request.sector;
+      if (SCpnt->request.sector > maxsec) maxsec = SCpnt->request.sector;
+
+      sl[n] = SCpnt->request.sector;
+
+      if (!n) continue;
+
+      if (sl[n] < sl[n - 1]) s = FALSE;
+      if (sl[n] > sl[n - 1]) r = FALSE;
+   
+      if (link_statistics) {
+         if (sl[n] > sl[n - 1])
+            seek += sl[n] - sl[n - 1];
+         else
+            seek += sl[n - 1] - sl[n];
+         }
+
+      }
+
+   if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
+
+   if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
+
+   if (!input_only) for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+      ll[n] = SCpnt->request.nr_sectors; pl[n] = SCpnt->pid;
+
+      if (!n) continue;
+ 
+      if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+          || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
+      }
+
+   if (overlap) sort(pl, il, n_ready, FALSE);
+
+   if (link_statistics) {
+      batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
+      if (input_only) inputcount++;
+      if (overlap) { ovlcount++; seeksorted += seek / 1024; }
+      else seeksorted += (maxsec - minsec) / 1024;
+      if (rev && !r)     {  revcount++; readysorted += n_ready; }
+      if (!rev && !s)    { sortcount++; readysorted += n_ready; }
+      }
+
+#if defined (DEBUG_LINKED_COMMANDS)
+   if (link_statistics && (overlap || !(flushcount % link_statistics)))
+      for (n = 0; n < n_ready; n++) {
+         k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+         printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+                " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+                (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
+                SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
+                SCpnt->request.sector, SCpnt->request.nr_sectors, cursec,
+                YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), 
+                YESNO(overlap), cpp->din);
+         }
+#endif
+}
+
+static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
+                      unsigned int ihdlr) {
+   Scsi_Cmnd *SCpnt;
+   struct mscp *cpp;
+   unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
 
    for (k = 0; k < sh[j]->can_queue; k++) {
 
-      if (HD(j)->cp_stat[k] != READY) continue;
+      if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue;
 
-      cpp = &HD(j)->cp[k];
+      cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
-      if (cpp->link_id != i) continue;
+      if (SCpnt->device != dev) continue;
 
-      SCpnt = cpp->SCpnt;
-      n_ready++;
+      if (HD(j)->cp_stat[k] == IN_USE) return;
+
+      il[n_ready++] = k;
+      }
+
+   reorder(j, cursec, ihdlr, il, n_ready);
+  
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
       if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
-         printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d, link_id %d, "\
-                "n_ready %d, adapter busy, will abort.\n", BN(j),
-                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                k, i, n_ready);
+         printk("%s: %s, target %d.%d:%d, pid %ld, Mbox %d, adapter"\
+                " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
+                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
          HD(j)->cp_stat[k] = ABORTING;
          continue;
          }
 
       HD(j)->cp_stat[k] = IN_USE;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-      printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d in use, link_id %d,"
-             " n_ready %d.\n", BN(j), SCpnt->channel, SCpnt->target, 
-             SCpnt->lun, SCpnt->pid, k, i, n_ready);
-#endif
       }
 
 }
@@ -1313,7 +1601,7 @@
                                      struct pt_regs *regs) {
    Scsi_Cmnd *SCpnt;
    unsigned long flags;
-   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0;
+   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg;
    struct mssp *spp;
    struct mscp *cpp;
 
@@ -1345,7 +1633,7 @@
 			      BN(j), HD(j)->iocount);
    
 	 /* Read the status register to clear the interrupt indication */
-	 inb(sh[j]->io_port + REG_STATUS);
+	 reg = inb(sh[j]->io_port + REG_STATUS);
    
 	 /* Service all mailboxes of this board */
 	 for (i = 0; i < sh[j]->can_queue; i++) {
@@ -1356,8 +1644,6 @@
    
 	    spp->eoc = FALSE;
    
-            if (linked_commands) process_ready_list(i, j);
-
 	    if (HD(j)->cp_stat[i] == IGNORE) {
 	       HD(j)->cp_stat[i] = FREE;
 	       continue;
@@ -1398,6 +1684,10 @@
 		     " irq %d.\n", BN(j), i, SCpnt->pid, 
 		     *(unsigned int *)SCpnt->host_scribble, irq);
    
+         if (linked_comm && SCpnt->device->queue_depth > 2
+                                           && TLDEV(SCpnt->device->type))
+            flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
 	    tstatus = status_byte(spp->target_status);
    
 	    switch (spp->adapter_status) {
@@ -1466,14 +1756,14 @@
 		     status = DID_ERROR << 16;
 
 		  break;
-	       case 0x07:     /* Bus Parity Error */
-	       case 0x0c:     /* Controller Ram Parity */
 	       case 0x05:     /* Unexpected Bus Phase */
 	       case 0x06:     /* Unexpected Bus Free */
+	       case 0x07:     /* Bus Parity Error */
 	       case 0x08:     /* SCSI Hung */
 	       case 0x09:     /* Unexpected Message Reject */
 	       case 0x0a:     /* SCSI Bus Reset Stuck */
 	       case 0x0b:     /* Auto Request-Sense Failed */
+	       case 0x0c:     /* Controller Ram Parity Error */
 	       default:
 		  status = DID_ERROR << 16;
 		  break;
@@ -1493,10 +1783,10 @@
 		do_trace || msg_byte(spp->target_status))
 #endif
 	       printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
-		      " target %d.%d:%d, pid %ld, count %d.\n",
+		      " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
 		      BN(j), i, spp->adapter_status, spp->target_status,
 		      SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                      HD(j)->iocount);
+                      reg, HD(j)->iocount);
    
 	    /* Set the command state to inactive */
 	    SCpnt->host_scribble = NULL;
diff -u --recursive --new-file v2.1.26/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v2.1.26/linux/drivers/scsi/eata.h	Tue Jan 14 02:48:50 1997
+++ linux/drivers/scsi/eata.h	Wed Feb 26 10:56:28 1997
@@ -12,7 +12,7 @@
 int eata2x_abort(Scsi_Cmnd *);
 int eata2x_reset(Scsi_Cmnd *, unsigned int);
 
-#define EATA_VERSION "2.50.00"
+#define EATA_VERSION "3.00.09"
 
 
 #define EATA {                                                 \
diff -u --recursive --new-file v2.1.26/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v2.1.26/linux/drivers/scsi/u14-34f.c	Tue Jan 14 02:48:50 1997
+++ linux/drivers/scsi/u14-34f.c	Wed Feb 26 10:56:28 1997
@@ -1,6 +1,21 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ *          When loading as a module, parameter passing is now supported
+ *          both in 2.0 and in 2.1 style.
+ *          Fixed data transfer direction for some SCSI opcodes.
+ *          Immediate acknowledge to request sense commands.
+ *          Linked commands to each disk device are now reordered by elevator
+ *          sorting. Rare cases in which reordering of write requests could 
+ *          cause wrong results are managed.
+ *
+ *      18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ *          Added command line options to enable/disable linked commands
+ *          (lc:[y|n]), old firmware support (of:[y|n]) and to set the max
+ *          queue depth (mq:xx). Default is "u14-34f=lc:n,of:n,mq:8".
+ *          Improved command linking.
+ *
  *       8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
  *          Added linked command support.
  *
@@ -9,7 +24,7 @@
  *
  *      22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
  *          The list of i/o ports to be probed can be overwritten by the
- *          "u14-34f=port0, port1,...." boot command line option.
+ *          "u14-34f=port0,port1,...." boot command line option.
  *          Scatter/gather lists are now allocated by a number of kmalloc
  *          calls, in order to avoid the previous size limit of 64Kb.
  *
@@ -118,8 +133,8 @@
  *
  *  Here a sample configuration using two U14F boards:
  *
- U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, UC 1, LC 1, MQ 8.
- U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, UC 1, LC 1, MQ 8.
+ U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, of:n, lc:y, mq:8.
+ U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, of:n, lc:y, mq:8.
  *
  *  The boot controller must have its BIOS enabled, while other boards can
  *  have their BIOS disabled, or enabled to an higher address.
@@ -167,21 +182,80 @@
  *  problems when using more then 16 scatter/gather lists.
  *
  *  The list of i/o ports to be probed can be totally replaced by the
- *  boot command line option: "u14-34f=port0, port1, port2,...", where the
+ *  boot command line option: "u14-34f=port0,port1,port2,...", where the
  *  port0, port1... arguments are ISA/VESA addresses to be probed.
- *  For example using "u14-34f=0x230, 0x340", the driver probes only the two
+ *  For example using "u14-34f=0x230,0x340", the driver probes only the two
  *  addresses 0x230 and 0x340 in this order; "u14-34f=0" totally disables
  *  this driver.
  *
+ *  After the optional list of detection probes, other possible command line
+ *  options are:
+ *
+ *  lc:y  enables linked commands;
+ *  lc:n  disables linked commands;
+ *  of:y  enables old firmware support;
+ *  of:n  disables old firmware support;
+ *  mq:xx set the max queue depth to the value xx (2 <= xx <= 8).
+ *
+ *  The default value is: "u14-34f=lc:n,of:n,mq:8". An example using the list
+ *  of detection probes could be: "u14-34f=0x230,0x340,lc:y,of:n,mq:4".
+ *
+ *  When loading as a module, parameters can be specified as well.
+ *  The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ *  modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \
+ *                max_queue_depth=4
+ *
+ *  ----------------------------------------------------------------------------
+ *  In this implementation, linked commands are designed to work with any DISK
+ *  or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ *  and reordering by elevator sorting commands directed to each device,
+ *  without any relation with the actual SCSI protocol between the controller
+ *  and the device.
+ *  If Q is the queue depth reported at boot time for each device (also named
+ *  cmds/lun) and Q > 2, whenever there is already an active command to the
+ *  device all other commands to the same device  (up to Q-1) are kept waiting
+ *  in the elevator sorting queue. When the active command completes, the
+ *  commands in this queue are sorted by sector address. The sort is chosen
+ *  between increasing or decreasing by minimizing the seek distance between
+ *  the sector of the commands just completed and the sector of the first 
+ *  command in the list to be sorted. 
+ *  Trivial math assures that if there are (Q-1) outstanding request for
+ *  random seeks over S sectors, the unsorted average seek distance is S/2,
+ *  while the sorted average seek distance is S/(Q-1). The seek distance is
+ *  hence divided by a factor (Q-1)/2.
+ *  The above pure geometric remarks are valid in all cases and the 
+ *  driver effectively reduces the seek distance by the predicted factor
+ *  when there are Q concurrent read i/o operations on the device, but this
+ *  does not necessarily results in a noticeable performance improvement:
+ *  your mileage may vary....
+ *
+ *  Note: command reordering inside a batch of queued commands could cause
+ *        wrong results only if there is at least one write request and the
+ *        intersection (sector-wise) of all requests is not empty. 
+ *        When the driver detects a batch including overlapping requests
+ *        (a really rare event) strict serial (pid) order is enforced.
+ *  ----------------------------------------------------------------------------
+ *
  *  The boards are named Ux4F0, Ux4F1,... according to the detection order.
  *
  *  In order to support multiple ISA boards in a reliable way,
  *  the driver sets host->wish_block = TRUE for all ISA boards.
  */
 
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define MAX_INT_PARAM 10
 #if defined(MODULE)
 #include <linux/module.h>
 #include <linux/version.h>
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
+MODULE_PARM(linked_comm, "i");
+MODULE_PARM(have_old_firmware, "i");
+MODULE_PARM(link_statistics, "i");
+MODULE_PARM(max_queue_depth, "i");
+MODULE_AUTHOR("Dario Ballabio");
+#endif
 #endif
 
 #include <linux/string.h>
@@ -247,8 +321,9 @@
 #define MAX_SAFE_SGLIST 16
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
-#define MAX_TAGGED_CMD_PER_LUN 8
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
 
+#define SKIP UINT_MAX
 #define FALSE 0
 #define TRUE 1
 #define FREE 0
@@ -282,6 +357,8 @@
 #define ASST              0x91
 
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
 
 #define PACKED          __attribute__((packed))
 
@@ -313,7 +390,6 @@
    unsigned int sense_addr PACKED;
    Scsi_Cmnd *SCpnt;
    unsigned int index;                  /* cp index */
-   unsigned int link_id;                /* reference cp for linked commands */
    struct sg_list *sglist;
    };
 
@@ -343,12 +419,18 @@
 static const char *driver_name = "Ux4F";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[] = {
-      0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140,
+static unsigned int io_port[] = { 
 
-      /* End of list */
-      0x0
-      };
+   /* Space for MAX_INT_PARAM ports usable while loading as a module */
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,
+
+   /* Possible ISA/VESA ports */
+   0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140,
+
+   /* End of list */
+   0x0
+   };
 
 #define HD(board) ((struct hostdata *) &sh[board]->hostdata)
 #define BN(board) (HD(board)->board_name)
@@ -370,13 +452,21 @@
 #define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0)
 
 static void u14_34f_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int);
 static int do_trace = FALSE;
 static int setup_done = FALSE;
+static int link_statistics = 0;
+
+#if defined (HAVE_OLD_UX4F_FIRMWARE)
+static int have_old_firmware = TRUE;
+#else
+static int have_old_firmware = FALSE;
+#endif
 
 #if defined (CONFIG_SCSI_U14_34F_LINKED_COMMANDS)
-static int linked_commands = TRUE;
+static int linked_comm = TRUE;
 #else
-static int linked_commands = FALSE;
+static int linked_comm = FALSE;
 #endif
 
 #if defined CONFIG_SCSI_U14_34F_MAX_TAGS
@@ -399,32 +489,45 @@
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) ntag++;
-      else                       nuntag++;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         ntag++;
+      else
+         nuntag++;
       }
 
    utqd = MAX_CMD_PER_LUN;
 
-   tqd = (host->can_queue - utqd * nuntag) / (ntag + 1);
+   tqd = (host->can_queue - utqd * nuntag) / (ntag ? ntag : 1);
 
    if (tqd > max_queue_depth) tqd = max_queue_depth;
 
    if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
 
    for(dev = devlist; dev; dev = dev->next) {
-      char *tag_suffix = "";
+      char *tag_suffix = "", *link_suffix = "";
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) dev->queue_depth = tqd;
-      else                       dev->queue_depth = utqd;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         dev->queue_depth = tqd;
+      else
+         dev->queue_depth = utqd;
 
-      if (dev->tagged_supported && dev->tagged_queue) tag_suffix = ", tagged";
-      else if (dev->tagged_supported) tag_suffix = ", untagged";
+      if (TLDEV(dev->type)) {
+         if (linked_comm && dev->queue_depth > 2)
+            link_suffix = ", linked";
+         else
+            link_suffix = ", unlinked";
+         }
 
-      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s.\n",
+      if (dev->tagged_supported && TLDEV(dev->type) && dev->tagged_queue)
+         tag_suffix = ", tagged";
+      else if (dev->tagged_supported && TLDEV(dev->type))
+         tag_suffix = ", untagged";
+
+      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
              BN(j), host->host_no, dev->channel, dev->id, dev->lun,
-             dev->queue_depth, tag_suffix);
+             dev->queue_depth, link_suffix, tag_suffix);
       }
 
    restore_flags(flags);
@@ -606,32 +709,26 @@
    HD(j)->board_number = j;
    irqlist[irq]++;
 
-   if (HD(j)->subversion == ESA) {
+   if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
 
-#if defined (HAVE_OLD_UX4F_FIRMWARE)
-      sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
-#endif
-
-      sh[j]->dma_channel = NO_DMA;
+   if (HD(j)->subversion == ESA) {
       sh[j]->unchecked_isa_dma = FALSE;
+      sh[j]->dma_channel = NO_DMA;
       sprintf(BN(j), "U34F%d", j);
       bus_type = "VESA";
       }
    else {
       sh[j]->wish_block = TRUE;
-
-#if defined (HAVE_OLD_UX4F_FIRMWARE)
-      sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
-      sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
-#endif
-
-      sh[j]->dma_channel = dma_channel;
       sh[j]->unchecked_isa_dma = TRUE;
-      sprintf(BN(j), "U14F%d", j);
       disable_dma(dma_channel);
       clear_dma_ff(dma_channel);
       set_dma_mode(dma_channel, DMA_MODE_CASCADE);
       enable_dma(dma_channel);
+
+      if (have_old_firmware) sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+
+      sh[j]->dma_channel = dma_channel;
+      sprintf(BN(j), "U14F%d", j);
       bus_type = "ISA";
       }
 
@@ -668,10 +765,10 @@
 
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
 
-   printk("%s: %s 0x%03x, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, UC %d, "\
-          "LC %d, MQ %d.\n", BN(j), bus_type, sh[j]->io_port, (int)sh[j]->base,
+   printk("%s: %s 0x%03x, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, of:%c, "\
+          "lc:%c, mq:%d.\n", BN(j), bus_type, sh[j]->io_port, (int)sh[j]->base,
           sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
-          sh[j]->hostt->use_clustering, linked_commands, max_queue_depth);
+          YESNO(have_old_firmware), YESNO(linked_comm), max_queue_depth);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -686,15 +783,33 @@
 
 void u14_34f_setup(char *str, int *ints) {
    int i, argc = ints[0];
+   char *cur = str, *pc;
 
-   if (argc <= 0) return;
+   if (argc > 0) {
 
-   if (argc > MAX_BOARDS) argc = MAX_BOARDS;
+      if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
 
-   for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
+      for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
    
-   io_port[i] = 0;
-   setup_done = TRUE;
+      io_port[i] = 0;
+      setup_done = TRUE;
+      }
+
+   while (cur && (pc = strchr(cur, ':'))) {
+      int val = 0, c = *++pc;
+
+      if (c == 'n' || c == 'N') val = FALSE;
+      else if (c == 'y' || c == 'Y') val = TRUE;
+      else val = (int) simple_strtoul(pc, NULL, 0);
+
+      if (!strncmp(cur, "lc:", 3)) linked_comm = val;
+      else if (!strncmp(cur, "of:", 3)) have_old_firmware = val;
+      else if (!strncmp(cur, "mq:", 3))  max_queue_depth = val;
+      else if (!strncmp(cur, "ls:", 3))  link_statistics = val;
+
+      if ((cur = strchr(cur, ','))) ++cur;
+      }
+
    return;
 }
 
@@ -707,6 +822,14 @@
    save_flags(flags);
    cli();
 
+#if defined(MODULE)
+   /* io_port could have been modified when loading as a module */
+   if(io_port[0] != SKIP) {
+      setup_done = TRUE;
+      io_port[MAX_INT_PARAM] = 0;
+      }
+#endif
+
    for (k = 0; k < MAX_IRQ; k++) {
       irqlist[k] = 0;
       calls[k] = 0;
@@ -714,8 +837,12 @@
 
    for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
 
-   for (k = 0; io_port[k]; k++)
+   for (k = 0; io_port[k]; k++) {
+
+      if (io_port[k] == SKIP) continue;
+
       if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++;
+      }
 
    if (j > 0) 
       printk("UltraStor 14F/34F: Copyright (C) 1994-1997 Dario Ballabio.\n");
@@ -747,9 +874,15 @@
    struct mscp *cpp;
 
    static const unsigned char data_out_cmds[] = {
-      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x0b, 0x10, 0x16, 0x18, 0x1d, 
-      0x24, 0x2b, 0x2e, 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 
-      0x3f, 0x40, 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea
+      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+      0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40, 
+      0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b
+      };
+
+   static const unsigned char data_none_cmds[] = {
+      0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+      0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+      0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5
       };
 
    save_flags(flags);
@@ -759,6 +892,16 @@
 
    if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
 
+   if (SCpnt->cmnd[0] == REQUEST_SENSE && SCpnt->sense_buffer[0]) {
+      SCpnt->result = DID_OK << 16; 
+      SCpnt->host_scribble = NULL;
+      printk("%s: qcomm, target %d.%d:%d, pid %ld, request sense ignored.\n",
+             BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid);
+      restore_flags(flags);
+      done(SCpnt);    
+      return 0;
+      }
+
    /* i is the mailbox number, look for the first free mailbox 
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
@@ -805,10 +948,17 @@
    cpp->xdir = DTD_IN;
 
    for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
-     if (SCpnt->cmnd[0] == data_out_cmds[k]) {
-	cpp->xdir = DTD_OUT;
-	break;
-	}
+      if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+	 cpp->xdir = DTD_OUT;
+	 break;
+	 }
+
+   if (cpp->xdir == DTD_IN)
+      for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+         if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+            cpp->xdir = DTD_NONE;
+            break;
+            }
 
    cpp->opcode = OP_SCSI;
    cpp->channel = SCpnt->channel;
@@ -830,27 +980,13 @@
    cpp->scsi_cdbs_len = SCpnt->cmd_len;
    memcpy(cpp->scsi_cdbs, SCpnt->cmnd, cpp->scsi_cdbs_len);
 
-   if (linked_commands && SCpnt->device->tagged_supported
-            && !HD(j)->target_to[SCpnt->target][SCpnt->channel]
-	    && !HD(j)->target_redo[SCpnt->target][SCpnt->channel])
-   
-      for (k = 0; k < sh[j]->can_queue; k++) {
- 
-         if (HD(j)->cp_stat[k] != IN_USE) continue;
-
-         if ((&HD(j)->cp[k])->SCpnt->device != SCpnt->device) continue;
-
-         cpp->link_id = k;
-         HD(j)->cp_stat[i] = READY;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-         printk("%s: qcomm, target %d.%d:%d, pid %ld, Mbox %d ready.\n", 
-                BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun,
-                SCpnt->pid, i);
-#endif
-         restore_flags(flags);
-         return 0;
-         }
+   if (linked_comm && SCpnt->device->queue_depth > 2
+                                     && TLDEV(SCpnt->device->type)) {
+      HD(j)->cp_stat[i] = READY;
+      flush_dev(SCpnt->device, 0, j, FALSE);
+      restore_flags(flags);
+      return 0;
+      }
 
    if (wait_on_busy(sh[j]->io_port)) {
       SCpnt->result = DID_ERROR << 16;
@@ -1102,27 +1238,147 @@
    return FALSE;
 }
 
-static inline void process_ready_list(unsigned int i, unsigned int j) {
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+                 unsigned int rev) {
+   unsigned int i, j, k, y;
+   unsigned long x;
+
+   for (i = 0; i < n - 1; i++) {
+      k = i;
+
+      for (j = k + 1; j < n; j++) 
+         if (rev) {
+            if (sk[j] > sk[k]) k = j;
+            }
+         else {
+            if (sk[j] < sk[k]) k = j;
+            }
+
+      if (k != i) {
+         x = sk[k]; sk[k] = sk[i]; sk[i] = x;
+         y = da[k]; da[k] = da[i]; da[i] = y;
+         }
+      }
+
+   return;
+   }
+
+static inline void reorder(unsigned int j, unsigned long cursec,
+                 unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
    Scsi_Cmnd *SCpnt;
-   unsigned int k, n_ready = 0;
    struct mscp *cpp;
+   unsigned int k, n;
+   unsigned int rev = FALSE, s = TRUE, r = TRUE;
+   unsigned int input_only = TRUE, overlap = FALSE;
+   unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+
+   static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+   static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+   static unsigned int readysorted = 0, revcount = 0;
+   static unsigned long seeksorted = 0, seeknosort = 0;
+
+   if (link_statistics && !(++flushcount % link_statistics))
+      printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
+             " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
+             ovlcount, readycount, readysorted, sortcount, revcount,
+             seeknosort / (readycount - batchcount + 1), 
+             seeksorted / (readycount - batchcount + 1));
+
+   if (n_ready <= 1) return;
+
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+      if (!(cpp->xdir == DTD_IN)) input_only = FALSE;
+
+      if (SCpnt->request.sector < minsec) minsec = SCpnt->request.sector;
+      if (SCpnt->request.sector > maxsec) maxsec = SCpnt->request.sector;
+
+      sl[n] = SCpnt->request.sector;
+
+      if (!n) continue;
+
+      if (sl[n] < sl[n - 1]) s = FALSE;
+      if (sl[n] > sl[n - 1]) r = FALSE;
+   
+      if (link_statistics) {
+         if (sl[n] > sl[n - 1])
+            seek += sl[n] - sl[n - 1];
+         else
+            seek += sl[n - 1] - sl[n];
+         }
+
+      }
+
+   if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
+
+   if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
+
+   if (!input_only) for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+      ll[n] = SCpnt->request.nr_sectors; pl[n] = SCpnt->pid;
+
+      if (!n) continue;
+ 
+      if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+          || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
+      }
+
+   if (overlap) sort(pl, il, n_ready, FALSE);
+
+   if (link_statistics) {
+      batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
+      if (input_only) inputcount++;
+      if (overlap) { ovlcount++; seeksorted += seek / 1024; }
+      else seeksorted += (maxsec - minsec) / 1024;
+      if (rev && !r)     {  revcount++; readysorted += n_ready; }
+      if (!rev && !s)    { sortcount++; readysorted += n_ready; }
+      }
+
+#if defined (DEBUG_LINKED_COMMANDS)
+   if (link_statistics && (overlap || !(flushcount % link_statistics)))
+      for (n = 0; n < n_ready; n++) {
+         k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+         printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+                " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+                (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
+                SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
+                SCpnt->request.sector, SCpnt->request.nr_sectors, cursec,
+                YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), 
+                YESNO(overlap), cpp->xdir);
+         }
+#endif
+}
+
+static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
+                      unsigned int ihdlr) {
+   Scsi_Cmnd *SCpnt;
+   struct mscp *cpp;
+   unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
 
    for (k = 0; k < sh[j]->can_queue; k++) {
 
-      if (HD(j)->cp_stat[k] != READY) continue;
+      if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue;
+
+      cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+      if (SCpnt->device != dev) continue;
 
-      cpp = &HD(j)->cp[k];
+      if (HD(j)->cp_stat[k] == IN_USE) return;
 
-      if (cpp->link_id != i) continue;
+      il[n_ready++] = k;
+      }
 
-      SCpnt = cpp->SCpnt;
-      n_ready++;
+   reorder(j, cursec, ihdlr, il, n_ready);
+  
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
       if (wait_on_busy(sh[j]->io_port)) {
-         printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d, link_id %d, "\
-                "n_ready %d, adapter busy, will abort.\n", BN(j),
-                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                k, i, n_ready);
+         printk("%s: %s, target %d.%d:%d, pid %ld, Mbox %d, adapter"\
+                " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
+                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
          HD(j)->cp_stat[k] = ABORTING;
          continue;
          }
@@ -1130,21 +1386,14 @@
       outl(V2DEV(cpp), sh[j]->io_port + REG_OGM);
       outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
       HD(j)->cp_stat[k] = IN_USE;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-      printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d in use, link_id %d,"
-             " n_ready %d.\n", BN(j), SCpnt->channel, SCpnt->target, 
-             SCpnt->lun, SCpnt->pid, k, i, n_ready);
-#endif
       }
-
 }
 
 static void u14_34f_interrupt_handler(int irq, void *dev_id,
                                       struct pt_regs *regs) {
    Scsi_Cmnd *SCpnt;
    unsigned long flags;
-   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, ret;
+   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg, ret;
    struct mscp *spp;
 
    save_flags(flags);
@@ -1167,7 +1416,7 @@
       loops = 0;
 
       /* Loop until all interrupts for a board are serviced */
-      while (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED) {
+      while ((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED) {
 	 total_loops++;
 	 loops++;
 
@@ -1186,8 +1435,6 @@
 	    panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n",
                   BN(j), (void *)ret, HD(j)->cp);
 
-         if (linked_commands) process_ready_list(i, j);
-
 	 if (HD(j)->cp_stat[i] == IGNORE) {
 	    HD(j)->cp_stat[i] = FREE;
 	    continue;
@@ -1223,6 +1470,10 @@
 		  " irq %d.\n", BN(j), i, SCpnt->pid, 
 		  *(unsigned int *)SCpnt->host_scribble, irq);
 
+         if (linked_comm && SCpnt->device->queue_depth > 2
+                                           && TLDEV(SCpnt->device->type))
+            flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
 	 tstatus = status_byte(spp->target_status);
 
 	 switch (spp->adapter_status) {
@@ -1321,10 +1572,10 @@
               do_trace || msg_byte(spp->target_status))
 #endif
 	    printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
-		   " target %d.%d:%d, pid %ld, count %d.\n",
+		   " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
 		   BN(j), i, spp->adapter_status, spp->target_status,
 		   SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                   HD(j)->iocount);
+                   reg, HD(j)->iocount);
 
 	 /* Set the command state to inactive */
 	 SCpnt->host_scribble = NULL;
diff -u --recursive --new-file v2.1.26/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v2.1.26/linux/drivers/scsi/u14-34f.h	Tue Jan 14 02:48:50 1997
+++ linux/drivers/scsi/u14-34f.h	Wed Feb 26 10:56:28 1997
@@ -11,7 +11,7 @@
 int u14_34f_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "2.50.00"
+#define U14_34F_VERSION "3.00.09"
 
 #define ULTRASTOR_14_34F {                                            \
 		NULL, /* Ptr for modules */                           \
diff -u --recursive --new-file v2.1.26/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.26/linux/fs/dcache.c	Mon Dec 23 00:08:12 1996
+++ linux/fs/dcache.c	Tue Feb 25 16:34:44 1997
@@ -21,6 +21,8 @@
 #include <linux/fs.h>
 #include <linux/string.h>
 
+#include <asm/unaligned.h>
+
 /*
  * Don't bother caching long names.. They just take up space in the cache, and
  * for a name cache you just want to cache the "normal" names anyway which tend
@@ -28,6 +30,7 @@
  */
 #define DCACHE_NAME_LEN	15
 #define DCACHE_SIZE 1024
+#define DCACHE_HASH_QUEUES 256
 
 struct hash_list {
 	struct dir_cache_entry * next;
@@ -69,7 +72,6 @@
  * The hash-queues are also doubly-linked circular lists, but the head is
  * itself on the doubly-linked list, not just a pointer to the first entry.
  */
-#define DCACHE_HASH_QUEUES 32
 #define hash_fn(dev,dir,namehash) ((HASHDEV(dev) ^ (dir) ^ (namehash)) % DCACHE_HASH_QUEUES)
 
 static struct hash_list hash_table[DCACHE_HASH_QUEUES];
@@ -109,6 +111,10 @@
  */
 static inline unsigned long namehash(const char * name, int len)
 {
+	if (len >= sizeof(unsigned long))
+		return len +
+			get_unaligned((unsigned long *) name) +
+			get_unaligned((unsigned long *) (name + len - sizeof(unsigned long)));
 	return len +
 		((const unsigned char *) name)[0]+
 		((const unsigned char *) name)[len-1];
@@ -143,16 +149,22 @@
  */
 static inline struct dir_cache_entry * find_entry(struct inode * dir, const char * name, int len, struct hash_list * hash)
 {
-	struct dir_cache_entry * de = hash->next;
+	struct dir_cache_entry * nextde = hash->next;
+
+	for (;;) {
+		struct dir_cache_entry * de;
 
-	for (de = hash->next ; de != (struct dir_cache_entry *) hash ; de = de->h.next) {
+		if (nextde == (struct dir_cache_entry *) hash)
+			break;
+		de = nextde;
+		nextde = nextde->h.next;
+		if (de->name_len != (unsigned char) len)
+			continue;
 		if (de->dc_dev != dir->i_dev)
 			continue;
 		if (de->dir != dir->i_ino)
 			continue;
 		if (de->version != dir->i_version)
-			continue;
-		if (de->name_len != len)
 			continue;
 		if (memcmp(de->name, name, len))
 			continue;
diff -u --recursive --new-file v2.1.26/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.1.26/linux/fs/filesystems.c	Sun Jan 26 02:07:44 1997
+++ linux/fs/filesystems.c	Fri Feb 21 14:58:35 1997
@@ -54,6 +54,10 @@
 	init_minix_fs();
 #endif
 
+#ifdef CONFIG_ROMFS_FS
+	init_romfs_fs();
+#endif
+
 #ifdef CONFIG_UMSDOS_FS
 	init_umsdos_fs();
 #endif
@@ -104,10 +108,6 @@
 
 #ifdef CONFIG_UFS_FS
 	init_ufs_fs();
-#endif
-
-#ifdef CONFIG_ROMFS_FS
-	init_romfs_fs();
 #endif
 
 	mount_root();
diff -u --recursive --new-file v2.1.26/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v2.1.26/linux/fs/nfs/nfsroot.c	Fri Feb  7 05:54:54 1997
+++ linux/fs/nfs/nfsroot.c	Fri Feb 21 14:58:35 1997
@@ -1351,6 +1351,7 @@
 			root_dev->pa_addr | ~root_dev->pa_mask;
 		devinet_ioctl(SIOCSIFBRDADDR, &ifr);
 	}
+	netmask.sin_addr.s_addr = root_dev->pa_mask;
 	set_fs(fs);
 
 	/*
diff -u --recursive --new-file v2.1.26/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.1.26/linux/include/asm-i386/irq.h	Sun Jan 26 02:07:46 1997
+++ linux/include/asm-i386/irq.h	Fri Feb 21 14:58:35 1997
@@ -164,7 +164,7 @@
 	"addl $8,%esp\n\t" \
 	"cli\n\t" \
 	UNBLK_##chip(mask) \
-	"jmp ret_from_sys_call\n" \
+	"jmp ret_from_intr\n" \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
 	SAVE_MOST \
@@ -195,7 +195,7 @@
 	"call "SYMBOL_NAME_STR(smp_reschedule_irq)"\n\t" \
 	"addl $8,%esp\n\t" \
 	"cli\n\t" \
-	"jmp ret_from_sys_call\n");
+	"jmp ret_from_intr\n");
 
 #endif /* __SMP__ */
 
@@ -217,7 +217,7 @@
 	"addl $8,%esp\n\t" \
 	"cli\n\t" \
 	UNBLK_##chip(mask) \
-	"jmp ret_from_sys_call\n" \
+	"jmp ret_from_intr\n" \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
 	SAVE_MOST \
@@ -253,6 +253,6 @@
 	"addl $8,%esp\n\t" \
 	"cli\n\t" \
 	UNBLK_##chip(mask) \
-	"jmp ret_from_sys_call\n");
+	"jmp ret_from_intr\n");
 
 #endif /* _ASM_IRQ_H */
diff -u --recursive --new-file v2.1.26/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h
--- v2.1.26/linux/include/asm-i386/smp.h	Sun Jan 26 05:36:05 1997
+++ linux/include/asm-i386/smp.h	Tue Feb 25 13:17:42 1997
@@ -227,10 +227,11 @@
  *	cpu id from the config and set up a fake apic_reg pointer so that before we activate
  *	the apic we get the right answer). Hopefully other processors are more sensible 8)
  */
- 
+
 extern __inline int smp_processor_id(void)
 {
-	return GET_APIC_ID(apic_read(APIC_ID));
+	/* we don't want to mark this access volatile - bad code generation */
+	return GET_APIC_ID(*(unsigned long *)(apic_reg+APIC_ID));
 }
 
 /* These read/change the "processes available" counter in the scheduler. */
diff -u --recursive --new-file v2.1.26/linux/include/asm-i386/string-486.h linux/include/asm-i386/string-486.h
--- v2.1.26/linux/include/asm-i386/string-486.h	Mon Oct 28 05:02:57 1996
+++ linux/include/asm-i386/string-486.h	Fri Feb 21 14:58:35 1997
@@ -339,7 +339,9 @@
 	"cmpl $-1,%2\n\t"
 	"jne 1b\n"
 	"3:\tsubl %1,%0"
-	:"=a" (__res):"c" (s),"d" (count));
+	:"=a" (__res)
+	:"c" (s),"d" (count)
+	:"dx");
 return __res;
 }
 /* end of additional stuff */
diff -u --recursive --new-file v2.1.26/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v2.1.26/linux/include/linux/ax25.h	Sun Feb  2 05:18:47 1997
+++ linux/include/linux/ax25.h	Tue Feb 25 17:12:50 1997
@@ -19,7 +19,6 @@
 #define	AX25_HDRINCL	8
 #define AX25_IDLE	9
 #define AX25_PACLEN	10
-#define AX25_MAXQUEUE	11
 #define AX25_IAMDIGI	12
 
 #define AX25_KILL	99
diff -u --recursive --new-file v2.1.26/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h
--- v2.1.26/linux/include/linux/hdlcdrv.h	Fri Dec 20 03:17:18 1996
+++ linux/include/linux/hdlcdrv.h	Fri Feb 21 14:58:35 1997
@@ -8,7 +8,10 @@
 #define _HDLCDRV_H
 
 #include <linux/sockios.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < 0x20119
 #include <linux/if_ether.h>
+#endif
 #include <linux/netdevice.h>
 
 /* -------------------------------------------------------------------- */
@@ -39,7 +42,11 @@
 	int ptt;
 	int dcd;
 	int ptt_keyed;
+#if LINUX_VERSION_CODE < 0x20119
 	struct enet_statistics stats;
+#else
+	struct net_device_stats stats;
+#endif
 };
 
 struct hdlcdrv_ioctl {
@@ -231,7 +238,11 @@
 	struct hdlcdrv_bitbuffer bitbuf_hdlc;
 #endif /* HDLCDRV_DEBUG */
 
+#if LINUX_VERSION_CODE < 0x20119
 	struct enet_statistics stats;
+#else
+	struct net_device_stats stats;
+#endif
 	int ptt_keyed;
 
 	struct sk_buff_head send_queue;  /* Packets awaiting transmission */
diff -u --recursive --new-file v2.1.26/linux/include/linux/if_tr.h linux/include/linux/if_tr.h
--- v2.1.26/linux/include/linux/if_tr.h	Fri Feb  7 05:54:55 1997
+++ linux/include/linux/if_tr.h	Fri Feb 21 14:58:36 1997
@@ -35,6 +35,7 @@
 
 
 /* These are some defined Ethernet Protocol ID's. */
+/* FIXME: should use the definitions in if_ether.h!!! */
 #define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
 #define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
 #define ETH_P_RARP      0x8035		/* Reverse Addr Res packet	*/
diff -u --recursive --new-file v2.1.26/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.1.26/linux/include/linux/interrupt.h	Fri Dec 20 02:06:14 1996
+++ linux/include/linux/interrupt.h	Wed Feb 26 13:35:27 1997
@@ -4,6 +4,7 @@
 
 #include <linux/kernel.h>
 #include <asm/bitops.h>
+#include <asm/atomic.h>
 
 struct irqaction {
 	void (*handler)(int, void *, struct pt_regs *);
@@ -14,7 +15,7 @@
 	struct irqaction *next;
 };
 
-extern unsigned long intr_count;
+extern atomic_t intr_count;
 
 extern int bh_mask_count[32];
 extern unsigned long bh_active;
@@ -75,14 +76,14 @@
  */
 extern inline void start_bh_atomic(void)
 {
-	intr_count++;
+	atomic_inc(&intr_count);
 	barrier();
 }
 
 extern inline void end_bh_atomic(void)
 {
 	barrier();
-	intr_count--;
+	atomic_dec(&intr_count);
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/include/linux/ipv6_route.h linux/include/linux/ipv6_route.h
--- v2.1.26/linux/include/linux/ipv6_route.h	Tue Nov 26 00:46:41 1996
+++ linux/include/linux/ipv6_route.h	Fri Feb 21 14:58:36 1997
@@ -13,8 +13,6 @@
 #ifndef _LINUX_IPV6_ROUTE_H
 #define _LINUX_IPV6_ROUTE_H
 
-#include <linux/route.h>
-
 #define RTI_DEVRT	0x00010000	/* route lookup, dev must match	*/
 #define RTI_ALLONLINK	0x00020000	/* all destinations on link	*/
 #define RTI_DCACHE	RTF_DCACHE	/* rt6_info is a dcache entry	*/
diff -u --recursive --new-file v2.1.26/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v2.1.26/linux/include/linux/isdn.h	Sat Aug 31 09:01:49 1996
+++ linux/include/linux/isdn.h	Tue Feb 25 17:12:50 1997
@@ -1,4 +1,4 @@
-/* $Id: isdn.h,v 1.16 1996/08/12 16:20:56 hipp Exp $
+/* $Id: isdn.h,v 1.23 1997/02/10 22:07:13 fritz Exp $
  *
  * Main header for the Linux ISDN subsystem (linklevel).
  *
@@ -21,6 +21,29 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdn.h,v $
+ * Revision 1.23  1997/02/10 22:07:13  fritz
+ * Added 2 modem registers for numbering plan and screening info.
+ *
+ * Revision 1.22  1997/02/03 23:42:08  fritz
+ * Added ISDN_TIMER_RINGING
+ * Misc. changes for Kernel 2.1.X compatibility
+ *
+ * Revision 1.21  1997/01/17 01:19:10  fritz
+ * Applied chargeint patch.
+ *
+ * Revision 1.20  1997/01/17 00:41:19  fritz
+ * Increased TTY_DV.
+ *
+ * Revision 1.19  1997/01/14 01:41:07  fritz
+ * Added ATI2 related variables.
+ * Added variables for audio support in skbuffs.
+ *
+ * Revision 1.18  1996/11/06 17:37:50  keil
+ * more changes for 2.1.X
+ *
+ * Revision 1.17  1996/09/07 12:53:57  hipp
+ * moved a few isdn_ppp.c specific defines to drives/isdn/isdn_ppp.h
+ *
  * Revision 1.16  1996/08/12 16:20:56  hipp
  * renamed ppp_minor to ppp_slot
  *
@@ -153,7 +176,7 @@
 #define ISDN_USAGE_EXCLUSIVE 64 /* This bit is set, if channel is exclusive */
 #define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing  */
 
-#define ISDN_MODEM_ANZREG    21        /* Number of Modem-Registers        */
+#define ISDN_MODEM_ANZREG    22        /* Number of Modem-Registers        */
 #define ISDN_MSNLEN          20
 
 typedef struct {
@@ -173,8 +196,8 @@
   int  outgoing;
 } isdn_net_ioctl_phone;
 
-#define NET_DV 0x01 /* Data version for net_cfg     */
-#define TTY_DV 0x01 /* Data version for iprofd etc. */
+#define NET_DV 0x02 /* Data version for net_cfg     */
+#define TTY_DV 0x03 /* Data version for iprofd etc. */
 
 typedef struct {
   char name[10];     /* Name of interface                     */
@@ -197,6 +220,7 @@
   int  callback;     /* Flag: Callback                        */
   int  cbhup;        /* Flag: Reject Call before Callback     */
   int  pppbind;      /* ippp device for bindings              */
+  int  chargeint;    /* Use fixed charge interval length      */
 } isdn_net_ioctl_cfg;
 
 #ifdef __KERNEL__
@@ -212,7 +236,6 @@
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/malloc.h>
-#include <linux/mm.h>
 #include <linux/timer.h>
 #include <linux/wait.h>
 #include <linux/tty.h>
@@ -274,6 +297,7 @@
 #define ISDN_TIMER_RES       3                     /* Main Timer-Resolution  */
 #define ISDN_TIMER_02SEC     (HZ/(ISDN_TIMER_RES+1)/5) /* Slow-Timer1 .2 sec */
 #define ISDN_TIMER_1SEC      (HZ/(ISDN_TIMER_RES+1)) /* Slow-Timer2 1 sec   */
+#define ISDN_TIMER_RINGING   5 /* tty RINGs = ISDN_TIMER_1SEC * this factor */
 #define ISDN_TIMER_MODEMREAD 1
 #define ISDN_TIMER_MODEMPLUS 2
 #define ISDN_TIMER_MODEMRING 4
@@ -356,6 +380,7 @@
 				       /* bit0: chargeint is invalid       */
 				       /* bit1: Getting charge-interval    */
                                        /* bit2: Do charge-unit-hangup      */
+                                       /* bit3: Do hangup even on incoming */
   int                    outgoing;     /* Flag: outgoing call              */
   int                    onhtime;      /* Time to keep link up             */
   int                    chargeint;    /* Interval between charge-infos    */
@@ -377,11 +402,21 @@
   struct isdn_net_dev_s  *netdev;      /* Ptr to netdev                    */
   struct sk_buff         *first_skb;   /* Ptr to skb that triggers dialing */
   struct sk_buff         *sav_skb;     /* Ptr to skb, rejected by LL-driver*/
+#if (LINUX_VERSION_CODE < 0x02010F)
                                        /* Ptr to orig. header_cache_bind   */
-  void                   (*org_hcb)(struct hh_cache **, struct device *,
-                                    unsigned short, __u32);
+  void                   (*org_hcb)(struct hh_cache **,
+				    struct device *,
+                                    unsigned short, 
+				    __u32);
+#else
+                                       /* Ptr to orig. hard_header_cache   */
+  int                    (*org_hhc)(struct dst_entry *dst,
+				    struct dst_entry *neigh,
+				    struct hh_cache *hh);
+#endif
                                        /* Ptr to orig. header_cache_update */
-  void                   (*org_hcu)(struct hh_cache *, struct device *,
+  void                   (*org_hcu)(struct hh_cache *,
+				    struct device *,
                                     unsigned char *);
   int  pppbind;                        /* ippp device for bindings         */
 } isdn_net_local;
@@ -430,6 +465,20 @@
 #define ISDN_SERIAL_TYPE_NORMAL            1
 #define ISDN_SERIAL_TYPE_CALLOUT           2
 
+/* For using sk_buffs with audio we need some private variables
+ * within each sk_buff. For this purpose, we declare a struct here,
+ * and put it always at skb->head. A few macros help accessing the
+ * variables. Of course, we need to check skb_headroom prior to
+ * any access.
+ */
+typedef struct isdn_audio_skb {
+  unsigned short dle_count;
+  unsigned char  lock;
+} isdn_audio_skb;
+
+#define ISDN_AUDIO_SKB_DLECOUNT(skb) (((isdn_audio_skb*)skb->head)->dle_count)
+#define ISDN_AUDIO_SKB_LOCK(skb) (((isdn_audio_skb*)skb->head)->lock)
+
 /* Private data of AT-command-interpreter */
 typedef struct atemu {
   u_char              profile[ISDN_MODEM_ANZREG]; /* Modem-Regs. Profile 0 */
@@ -465,6 +514,12 @@
   int                   isdn_channel;    /* Index to isdn-channel          */
   int                   drv_index;       /* Index to dev->usage            */
   int                   ncarrier;        /* Flag: schedule NO CARRIER      */
+  unsigned char         last_cause[8];   /* Last cause message             */
+  unsigned char         last_num[20];    /* Last phone-number              */
+  unsigned char         last_l2;         /* Last layer-2 protocol          */
+  unsigned char         last_si;         /* Last service                   */
+  unsigned char         last_lhup;       /* Last hangup local?             */
+  unsigned char         last_dir;        /* Last direction (in or out)     */
   struct timer_list     nc_timer;        /* Timer for delayed NO CARRIER   */
   int                   send_outstanding;/* # of outstanding send-requests */
   int                   xmit_size;       /* max. # of chars in xmit_buf    */
@@ -503,11 +558,6 @@
 
 #define NUM_RCV_BUFFS     64
 #define PPP_HARD_HDR_LEN 4
-
-#define IPPP_OPEN        0x1
-#define IPPP_CONNECT     0x2
-#define IPPP_CLOSEWAIT   0x4
-#define IPPP_NOBLOCK     0x8
 
 #ifdef CONFIG_ISDN_PPP
 
diff -u --recursive --new-file v2.1.26/linux/include/linux/isdn_ppp.h linux/include/linux/isdn_ppp.h
--- v2.1.26/linux/include/linux/isdn_ppp.h	Sun May 19 05:29:31 1996
+++ linux/include/linux/isdn_ppp.h	Tue Feb 25 17:12:50 1997
@@ -4,18 +4,20 @@
 extern int isdn_ppp_dial_slave(char *);
 extern int isdn_ppp_hangup_slave(char *);
 
-struct pppinfo
+#define CALLTYPE_INCOMING 0x1
+#define CALLTYPE_OUTGOING 0x2
+#define CALLTYPE_CALLBACK 0x4
+
+struct pppcallinfo
 {
-  int type; /* set by user */
-  union {
-    char clid[32]; /* calling ID */
-    int  bundles;
-    int  linknumber;
-  } info;
+	int calltype;
+	unsigned char local_num[64];
+	unsigned char remote_num[64];
+	int charge_units;
 };
 
  
-#define PPPIOCLINKINFO _IOWR('t',128,struct pppinfo)
+#define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo)
 #define PPPIOCBUNDLE   _IOW('t',129,int)
 #define PPPIOCGMPFLAGS _IOR('t',130,int)
 #define PPPIOCSMPFLAGS _IOW('t',131,int)
diff -u --recursive --new-file v2.1.26/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v2.1.26/linux/include/linux/isdnif.h	Fri Jun  7 06:02:43 1996
+++ linux/include/linux/isdnif.h	Tue Feb 25 17:12:50 1997
@@ -1,4 +1,4 @@
-/* $Id: isdnif.h,v 1.9 1996/06/06 21:24:24 fritz Exp $
+/* $Id: isdnif.h,v 1.17 1997/02/10 21:12:53 fritz Exp $
  *
  * Linux ISDN subsystem
  *
@@ -22,6 +22,30 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  *
  * $Log: isdnif.h,v $
+ * Revision 1.17  1997/02/10 21:12:53  fritz
+ * More setup-interface changes.
+ *
+ * Revision 1.16  1997/02/10 19:42:57  fritz
+ * New interface for reporting incoming calls.
+ *
+ * Revision 1.15  1997/02/09 00:18:42  keil
+ * leased line support
+ *
+ * Revision 1.14  1997/02/03 23:43:00  fritz
+ * Misc changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.13  1996/11/13 02:39:59  fritz
+ * More compatibility changes.
+ *
+ * Revision 1.12  1996/11/06 17:38:48  keil
+ * more changes for 2.1.X
+ *
+ * Revision 1.11  1996/10/23 11:59:42  fritz
+ * More compatibility changes.
+ *
+ * Revision 1.10  1996/10/22 23:14:19  fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
  * Revision 1.9  1996/06/06 21:24:24  fritz
  * Started adding support for suspend/resume.
  *
@@ -64,6 +88,7 @@
 #define ISDN_PTYPE_UNKNOWN   0   /* Protocol undefined   */
 #define ISDN_PTYPE_1TR6      1   /* german 1TR6-protocol */
 #define ISDN_PTYPE_EURO      2   /* EDSS1-protocol       */
+#define ISDN_PTYPE_LEASED    3   /* for leased lines     */
 
 /*
  * Values for Layer-2-protocol-selection
@@ -145,6 +170,15 @@
 #define ISDN_FEATURE_P_1TR6     (0x1000 << ISDN_PTYPE_1TR6)
 #define ISDN_FEATURE_P_EURO     (0x1000 << ISDN_PTYPE_EURO)
 
+typedef struct setup_parm {
+    char phone[32];         /* Remote Phone-Number */
+    char eazmsn[32];        /* Local EAZ or MSN    */
+    unsigned char si1;      /* Service Indicator 1 */
+    unsigned char si2;      /* Service Indicator 2 */
+    unsigned char plan;     /* Numbering plan      */
+    unsigned char screen;   /* Screening info      */
+} setup_parm;
+
 /*
  * Structure for exchanging above infos
  *
@@ -153,7 +187,10 @@
   int   driver;                  /* Lowlevel-Driver-ID                    */
   int   command;                 /* Command or Status (see above)         */
   ulong arg;                     /* Additional Data                       */
-  char  num[50];                 /* Additional Data                       */
+  union {
+	char  num[50];               /* Additional Data                       */
+	setup_parm setup;
+  } parm;
 } isdn_ctrl;
 
 /*
@@ -302,6 +339,35 @@
  */
 extern int register_isdn(isdn_if*);
 
+/* Compatibility Linux-2.0.X <-> Linux-2.1.X */
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+#if (LINUX_VERSION_CODE < 0x020100)
+#include <linux/mm.h>
+#define copy_from_user memcpy_fromfs
+#define copy_to_user memcpy_tofs
+#define GET_USER(x, addr) ( x = get_user(addr) )
+#define RWTYPE int
+#define LSTYPE int
+#define RWARG int
+#define LSARG off_t
+#define SET_SKB_FREE(x) ( x->free = 1 )
+#else
+#include <asm/uaccess.h>
+#define GET_USER get_user
+#define PUT_USER put_user
+#define RWTYPE long
+#define LSTYPE long long
+#define RWARG unsigned long
+#define LSARG long long
+#if (LINUX_VERSION_CODE < 0x02010F)
+#define SET_SKB_FREE(x) ( x->free = 1 )
+#else
+#define SET_SKB_FREE(x)
+#endif
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* isdnif_h */
-
diff -u --recursive --new-file v2.1.26/linux/include/linux/netrom.h linux/include/linux/netrom.h
--- v2.1.26/linux/include/linux/netrom.h	Sun Jan 19 05:47:26 1997
+++ linux/include/linux/netrom.h	Fri Feb 21 14:58:36 1997
@@ -14,7 +14,6 @@
 #define NETROM_T2	2
 #define NETROM_N2	3
 #define	NETROM_HDRINCL	4
-#define	NETROM_PACLEN	5
 #define	NETROM_T4	6
 #define	NETROM_IDLE	7
 
diff -u --recursive --new-file v2.1.26/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.26/linux/include/linux/pci.h	Fri Feb  7 05:54:55 1997
+++ linux/include/linux/pci.h	Fri Feb 21 14:58:36 1997
@@ -589,6 +589,9 @@
 #define PCI_DEVICE_ID_INTEL_82437VX	0x7030
 #define PCI_DEVICE_ID_INTEL_P6		0x84c4
 
+#define PCI_VENDOR_ID_KTI		0x8e2e
+#define PCI_DEVICE_ID_KTI_ET32P2	0x3000
+
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
 #define PCI_DEVICE_ID_ADAPTEC_7855	0x5578
diff -u --recursive --new-file v2.1.26/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.26/linux/include/linux/sysctl.h	Fri Feb  7 05:54:55 1997
+++ linux/include/linux/sysctl.h	Tue Feb 25 17:12:50 1997
@@ -69,6 +69,7 @@
 #define VM_FREEPG	3	/* struct: Set free page thresholds */
 #define VM_BDFLUSH	4	/* struct: Control buffer cache flushing */
 #define VM_MAXID	5
+#define VM_OVERCOMMIT_MEMORY 7	/* Turn off the virtual memory safety limit */
 
 /* CTL_NET names: */
 #define NET_CORE        1
@@ -144,7 +145,21 @@
 };
 
 /* /proc/sys/net/ax25 */
-/* Values are generated dynamically */
+enum {
+	NET_AX25_IP_DEFAULT_MODE = 1,
+	NET_AX25_DEFAULT_MODE,
+	NET_AX25_BACKOFF_TYPE,
+	NET_AX25_CONNECT_MODE,
+	NET_AX25_STANDARD_WINDOW,
+	NET_AX25_EXTENDED_WINDOW,
+	NET_AX25_T1_TIMEOUT,
+	NET_AX25_T2_TIMEOUT,
+	NET_AX25_T3_TIMEOUT,
+	NET_AX25_IDLE_TIMEOUT,
+	NET_AX25_N2,
+	NET_AX25_PACLEN,
+	NET_AX25_PROTOCOL
+};
 
 /* /proc/sys/net/rose */
 enum {
diff -u --recursive --new-file v2.1.26/linux/include/net/ax25.h linux/include/net/ax25.h
--- v2.1.26/linux/include/net/ax25.h	Thu Feb  6 02:55:48 1997
+++ linux/include/net/ax25.h	Wed Feb 26 14:08:31 1997
@@ -101,42 +101,43 @@
 #define AX25_RESPONSE		2
 
 /* Define Link State constants. */
-#define AX25_STATE_0		0
-#define AX25_STATE_1		1
-#define AX25_STATE_2		2
-#define AX25_STATE_3		3
-#define AX25_STATE_4		4
 
-#define	AX25_MAX_DEVICES	20	/* Max No of AX.25 devices */
+enum { 
+	AX25_STATE_0,
+	AX25_STATE_1,
+	AX25_STATE_2,
+	AX25_STATE_3,
+	AX25_STATE_4
+};
 
 #define AX25_MODULUS 		8	/*  Standard AX.25 modulus */
 #define	AX25_EMODULUS		128	/*  Extended AX.25 modulus */
 
-#define	AX25_DIGI_INBAND	0x01	/* Allow digipeating within port **/
-#define	AX25_DIGI_XBAND		0x02	/* Allow digipeating across ports **/
-
-#define	AX25_VALUES_IPDEFMODE	0	/* 0=DG 1=VC */
-#define	AX25_VALUES_AXDEFMODE	1	/* 0=Normal 1=Extended Seq Nos */
-#define	AX25_VALUES_TEXT	2	/* Allow PID=Text - 0=No 1=Yes */
-#define	AX25_VALUES_BACKOFF	3	/* 0=None 1=Linear 2=Exponential */
-#define	AX25_VALUES_CONMODE	4	/* Allow connected modes - 0=No 1=Yes */
-#define	AX25_VALUES_WINDOW	5	/* Default window size for standard AX.25 */
-#define	AX25_VALUES_EWINDOW	6	/* Default window size for extended AX.25 */
-#define	AX25_VALUES_T1		7	/* Default T1 timeout value */
-#define	AX25_VALUES_T2		8	/* Default T2 timeout value */
-#define	AX25_VALUES_T3		9	/* Default T3 timeout value */
-#define AX25_VALUES_IDLE	10	/* mode vc idle timer */
-#define	AX25_VALUES_N2		11	/* Default N2 value */
-#define AX25_VALUES_PACLEN	12	/* AX.25 MTU */
-#define AX25_VALUES_MAXQUEUE	13	/* Maximum number of buffers enqueued */
-#define	AX25_VALUES_DIGI	14	/* Digipeat mode */
-#define	AX25_MAX_VALUES		15
+#define	AX25_PROTO_STD		0
+#define	AX25_PROTO_DAMA_SLAVE	1
+#define	AX25_PROTO_DAMA_MASTER	2
+
+enum {
+	AX25_VALUES_IPDEFMODE,	/* 0=DG 1=VC */
+	AX25_VALUES_AXDEFMODE,	/* 0=Normal 1=Extended Seq Nos */
+	AX25_VALUES_BACKOFF,	/* 0=None 1=Linear 2=Exponential */
+	AX25_VALUES_CONMODE,	/* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */
+	AX25_VALUES_WINDOW,	/* Default window size for standard AX.25 */
+	AX25_VALUES_EWINDOW,	/* Default window size for extended AX.25 */
+	AX25_VALUES_T1,		/* Default T1 timeout value */
+	AX25_VALUES_T2,		/* Default T2 timeout value */
+	AX25_VALUES_T3,		/* Default T3 timeout value */
+	AX25_VALUES_IDLE,	/* Connected mode idle timer */
+	AX25_VALUES_N2,		/* Default N2 value */
+	AX25_VALUES_PACLEN,	/* AX.25 MTU */
+	AX25_VALUES_PROTOCOL,	/* Std AX.25, DAMA Slave, DAMA Master */
+	AX25_MAX_VALUES		/* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */
+};
 
 #define	AX25_DEF_IPDEFMODE	0			/* Datagram */
 #define	AX25_DEF_AXDEFMODE	0			/* Normal */
-#define	AX25_DEF_TEXT		1			/* PID=Text allowed */
 #define	AX25_DEF_BACKOFF	1			/* Linear backoff */
-#define	AX25_DEF_CONMODE	1			/* Connected mode allowed */
+#define	AX25_DEF_CONMODE	2			/* Connected mode allowed */
 #define	AX25_DEF_WINDOW		2			/* Window=2 */
 #define	AX25_DEF_EWINDOW	32			/* Module-128 Window=32 */
 #define	AX25_DEF_T1		(10 * AX25_SLOWHZ)	/* T1=10s */
@@ -145,27 +146,47 @@
 #define	AX25_DEF_N2		10			/* N2=10 */
 #define AX25_DEF_IDLE		(20 * 60 * AX25_SLOWHZ)	/* Idle=20 mins */		
 #define AX25_DEF_PACLEN		256			/* Paclen=256 */
-#define AX25_DEF_MAXQUEUE	2			/* 1 * ax25->window */
-#define	AX25_DEF_DIGI		0x03			/* All digis alowed */
+#define	AX25_DEF_PROTOCOL	AX25_PROTO_STD		/* Standard AX.25 */
 
 typedef struct ax25_uid_assoc {
-	struct ax25_uid_assoc *next;
-	uid_t uid;
-	ax25_address call;
+	struct ax25_uid_assoc	*next;
+	uid_t			uid;
+	ax25_address		call;
 } ax25_uid_assoc;
 
 typedef struct {
-	ax25_address calls[AX25_MAX_DIGIS];
-	unsigned char repeated[AX25_MAX_DIGIS];
-	unsigned char ndigi;
-	char lastrepeat;
+	ax25_address		calls[AX25_MAX_DIGIS];
+	unsigned char		repeated[AX25_MAX_DIGIS];
+	unsigned char		ndigi;
+	char			lastrepeat;
 } ax25_digi;
 
+typedef struct ax25_route {
+	struct ax25_route	*next;
+	ax25_address		callsign;
+	struct device		*dev;
+	ax25_digi		*digipeat;
+	char			ip_mode;
+} ax25_route;
+
+#ifndef _LINUX_SYSCTL_H
+#include <linux/sysctl.h>
+#endif
+
+typedef struct ax25_dev {
+	struct ax25_dev		*next;
+	struct device		*dev;
+	struct device		*forward;
+	struct ctl_table	systable[AX25_MAX_VALUES+1];
+	int			values[AX25_MAX_VALUES];
+} ax25_dev;
+
 typedef struct ax25_cb {
 	struct ax25_cb		*next;
 	ax25_address		source_addr, dest_addr;
-	struct device		*device;
-	unsigned char		dama_slave, iamdigi;
+	ax25_digi		*digipeat;
+	ax25_dev		*ax25_dev;
+	unsigned char		iamdigi;
 	unsigned char		state, modulus, hdrincl;
 	unsigned short		vs, vr, va;
 	unsigned char		condition, backoff;
@@ -173,9 +194,7 @@
 	unsigned short		t1, t2, t3, idle, rtt;
 	unsigned short		t1timer, t2timer, t3timer, idletimer;
 	unsigned short		paclen;
-	unsigned short		maxqueue;
 	unsigned short		fragno, fraglen;
-	ax25_digi		*digipeat;
 	struct sk_buff_head	write_queue;
 	struct sk_buff_head	reseq_queue;
 	struct sk_buff_head	ack_queue;
@@ -185,68 +204,110 @@
 	struct sock		*sk;		/* Backlink to socket */
 } ax25_cb;
 
-struct ax25_dev {
-	char name[20];
-	struct device *dev;
-	struct device *forward;
-	int values[AX25_MAX_VALUES];
-};
-
 /* af_ax25.c */
+extern ax25_cb *volatile ax25_list;
+extern void ax25_free_cb(ax25_cb *);
+extern void ax25_insert_socket(ax25_cb *);
+struct sock *ax25_find_listener(ax25_address *, int, struct device *, int);
+struct sock *ax25_find_socket(ax25_address *, ax25_address *, int);
+extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern struct sock *ax25_addr_match(ax25_address *);
+extern void ax25_send_to_raw(struct sock *, struct sk_buff *, int);
+extern void ax25_destroy_socket(ax25_cb *);
+extern ax25_cb *ax25_create_cb(void);
+extern void ax25_fillin_cb(ax25_cb *, ax25_dev *);
+extern int  ax25_create(struct socket *, int);
+extern struct sock *ax25_make_new(struct sock *, struct device *);
+
+/* ax25_addr.c */
 extern ax25_address null_ax25_address;
 extern char *ax2asc(ax25_address *);
 extern ax25_address *asc2ax(char *);
 extern int  ax25cmp(ax25_address *, ax25_address *);
-extern int  ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *);
-extern int  ax25_link_up(ax25_address *, ax25_address *, struct device *);
-extern void ax25_destroy_socket(ax25_cb *);
-extern struct device *ax25rtr_get_dev(ax25_address *);
-extern int  ax25_encapsulate(struct sk_buff *, struct device *, unsigned short,
-	void *, void *, unsigned int);
-extern int  ax25_rebuild_header(struct sk_buff *);
-extern ax25_uid_assoc *ax25_uid_list;
-extern int  ax25_uid_policy;
-extern ax25_address *ax25_findbyuid(uid_t);
-extern void ax25_queue_xmit(struct sk_buff *);
-extern int  ax25_dev_is_dama_slave(struct device *);	/* dl1bke 951121 */
+extern int  ax25digicmp(ax25_digi *, ax25_digi *);
+extern unsigned char *ax25_addr_parse(unsigned char *, int, ax25_address *, ax25_address *, ax25_digi *, int *, int *);
+extern int  ax25_addr_build(unsigned char *, ax25_address *, ax25_address *, ax25_digi *, int, int);
+extern int  ax25_addr_size(ax25_digi *);
+extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
+
+/* ax25_dev.c */
+extern ax25_dev *ax25_dev_list;
+extern ax25_dev *ax25_dev_ax25dev(struct device *);
+extern ax25_dev *ax25_addr_ax25dev(ax25_address *);
+extern void ax25_dev_device_up(struct device *);
+extern void ax25_dev_device_down(struct device *);
+extern int  ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *);
+extern struct device *ax25_fwd_dev(struct device *);
+extern void ax25_dev_free(void);
+
+/* ax25_ds_in.c */
+extern int  ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int);
+
+/* ax25_ds_subr.c */
+extern void ax25_ds_nr_error_recovery(ax25_cb *);
+extern void ax25_ds_enquiry_response(ax25_cb *);
+extern void ax25_ds_establish_data_link(ax25_cb *);
+extern void ax25_dama_on(ax25_cb *);
+extern void ax25_dama_off(ax25_cb *);
+
+/* ax25_ds_timer.c */
+extern void ax25_ds_timer(ax25_cb *);
+extern void ax25_ds_t1_timeout(ax25_cb *);
 
 #include <net/ax25call.h>
 
+/* ax25_iface.c */
+extern int  ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
+extern void ax25_protocol_release(unsigned int);
+extern int  ax25_linkfail_register(void (*)(ax25_address *, struct device *));
+extern void ax25_linkfail_release(void (*)(ax25_address *, struct device *));
+extern int  ax25_listen_register(ax25_address *, struct device *);
+extern void ax25_listen_release(ax25_address *, struct device *);
+extern int  (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
+extern int  ax25_listen_mine(ax25_address *, struct device *);
+extern void ax25_link_failed(ax25_address *, struct device *);
+extern int  ax25_link_up(ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern int  ax25_protocol_is_registered(unsigned int);
+
 /* ax25_in.c */
-extern int  ax25_process_rx_frame(ax25_cb *, struct sk_buff *, int, int);
+extern int  ax25_rx_iframe(ax25_cb *, struct sk_buff *);
+extern int  ax25_kiss_rcv(struct sk_buff *, struct device *, struct packet_type *);
+
+/* ax25_ip.c */
+extern int  ax25_encapsulate(struct sk_buff *, struct device *, unsigned short, void *, void *, unsigned int);
+extern int  ax25_rebuild_header(struct sk_buff *);
 
 /* ax25_out.c */
+extern int  ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *);
 extern void ax25_output(ax25_cb *, int, struct sk_buff *);
 extern void ax25_kick(ax25_cb *);
 extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int);
-extern void ax25_nr_error_recovery(ax25_cb *);
-extern void ax25_establish_data_link(ax25_cb *);
-extern void ax25_transmit_enquiry(ax25_cb *);
-extern void ax25_enquiry_response(ax25_cb *);
-extern void ax25_timeout_response(ax25_cb *);
+extern void ax25_queue_xmit(struct sk_buff *);
 extern void ax25_check_iframes_acked(ax25_cb *, unsigned short);
-extern void ax25_check_need_response(ax25_cb *, int, int);
-extern void dama_enquiry_response(ax25_cb *);			/* dl1bke 960114 */
-extern void dama_check_need_response(ax25_cb *, int, int);	/* dl1bke 960114 */
-extern void dama_establish_data_link(ax25_cb *);
 
 /* ax25_route.c */
-extern struct ax25_dev ax25_device[];
+extern void ax25_rt_device_down(struct device *);
+extern int  ax25_rt_ioctl(unsigned int, void *);
 extern int  ax25_rt_get_info(char *, char **, off_t, int, int);
-extern int  ax25_cs_get_info(char *, char **, off_t, int, int);
 extern int  ax25_rt_autobind(ax25_cb *, ax25_address *);
 extern void ax25_rt_build_path(ax25_cb *, ax25_address *, struct device *);
 extern void ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *);
-extern void ax25_rt_device_down(struct device *);
-extern int  ax25_rt_ioctl(unsigned int, void *);
 extern char ax25_ip_mode_get(ax25_address *, struct device *);
-extern int  ax25_dev_get_value(struct device *, int);
-extern void ax25_dev_device_up(struct device *);
-extern void ax25_dev_device_down(struct device *);
-extern int  ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *);
-extern struct device *ax25_fwd_dev(struct device *);
 extern void ax25_rt_free(void);
 
+/* ax25_std_in.c */
+extern int  ax25_std_frame_in(ax25_cb *, struct sk_buff *, int);
+
+/* ax25_std_subr.c */
+extern void ax25_std_nr_error_recovery(ax25_cb *);
+extern void ax25_std_establish_data_link(ax25_cb *);
+extern void ax25_std_transmit_enquiry(ax25_cb *);
+extern void ax25_std_enquiry_response(ax25_cb *);
+extern void ax25_std_timeout_response(ax25_cb *);
+
+/* ax25_std_timer.c */
+extern void ax25_std_timer(ax25_cb *);
+
 /* ax25_subr.c */
 extern void ax25_clear_queues(ax25_cb *);
 extern void ax25_frames_acked(ax25_cb *, unsigned short);
@@ -254,41 +315,22 @@
 extern int  ax25_validate_nr(ax25_cb *, unsigned short);
 extern int  ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *);
 extern void ax25_send_control(ax25_cb *, int, int, int);
+extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
 extern unsigned short ax25_calculate_t1(ax25_cb *);
 extern void ax25_calculate_rtt(ax25_cb *);
-extern unsigned char *ax25_parse_addr(unsigned char *, int, ax25_address *,
-	ax25_address *, ax25_digi *, int *, int *);	/* dl1bke 951121 */
-extern int  build_ax25_addr(unsigned char *, ax25_address *, ax25_address *,
-	ax25_digi *, int, int);
-extern int  size_ax25_addr(ax25_digi *);
-extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
-extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
-extern int  ax25_queue_length(ax25_cb *, struct sk_buff *); /* dl1bke 960327 */
-extern void ax25_dama_on(ax25_cb *);	/* dl1bke 951121 */
-extern void ax25_dama_off(ax25_cb *);	/* dl1bke 951121 */
 
 /* ax25_timer.c */
 extern void ax25_set_timer(ax25_cb *);
-extern void ax25_t1_timeout(ax25_cb *);
-extern void ax25_link_failed(ax25_address *, struct device *);
-extern int  (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
-extern int  ax25_listen_mine(ax25_address *, struct device *);
+
+/* ax25_uid.c */
+extern int  ax25_uid_policy;
+extern ax25_address *ax25_findbyuid(uid_t);
+extern int  ax25_uid_ioctl(int, struct sockaddr_ax25 *);
+extern int  ax25_uid_get_info(char *, char **, off_t, int, int);
+extern void ax25_uid_free(void);
 
 /* sysctl_net_ax25.c */
 extern void ax25_register_sysctl(void);
 extern void ax25_unregister_sysctl(void);
-
-/* ... */
-
-extern ax25_cb *volatile ax25_list;
-
-/* support routines for modules that use AX.25, in ax25_timer.c */
-extern int  ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
-extern void ax25_protocol_release(unsigned int);
-extern int  ax25_linkfail_register(void (*)(ax25_address *, struct device *));
-extern void ax25_linkfail_release(void (*)(ax25_address *, struct device *));
-extern int  ax25_listen_register(ax25_address *, struct device *);
-extern void ax25_listen_release(ax25_address *, struct device *);
-extern int  ax25_protocol_is_registered(unsigned int);
 
 #endif
diff -u --recursive --new-file v2.1.26/linux/include/net/lapb.h linux/include/net/lapb.h
--- v2.1.26/linux/include/net/lapb.h	Tue Feb  4 06:44:24 1997
+++ linux/include/net/lapb.h	Fri Feb 21 14:58:36 1997
@@ -31,6 +31,11 @@
 #define	LAPB_SPF	0x10	/* Poll/final bit for standard LAPB */
 #define	LAPB_EPF	0x01	/* Poll/final bit for extended LAPB */
 
+#define	LAPB_FRMR_W	0x01	/* Control field invalid	*/
+#define	LAPB_FRMR_X	0x02	/* I field invalid		*/
+#define	LAPB_FRMR_Y	0x04	/* I field too long		*/
+#define	LAPB_FRMR_Z	0x08	/* Invalid N(R)			*/
+
 #define	LAPB_POLLOFF	0
 #define	LAPB_POLLON	1
 
@@ -44,11 +49,13 @@
 #define	LAPB_ADDR_D	0x07
 
 /* Define Link State constants. */
-#define	LAPB_STATE_0	0		/* Disconnected State		*/
-#define	LAPB_STATE_1	1		/* Awaiting Connection State	*/
-#define	LAPB_STATE_2	2		/* Awaiting Disconnection State	*/
-#define	LAPB_STATE_3	3		/* Data Transfer State		*/
-#define	LAPB_STATE_4	4		/* Frame Reject State		*/
+enum {
+	LAPB_STATE_0,	/* Disconnected State		*/
+	LAPB_STATE_1,	/* Awaiting Connection State	*/
+	LAPB_STATE_2,	/* Awaiting Disconnection State	*/
+	LAPB_STATE_3,	/* Data Transfer State		*/
+	LAPB_STATE_4	/* Frame Reject State		*/
+};
 
 #define	LAPB_DEFAULT_MODE		(LAPB_STANDARD | LAPB_SLP | LAPB_DTE)
 #define	LAPB_DEFAULT_WINDOW		7			/* Window=7 */
@@ -59,9 +66,25 @@
 #define	LAPB_SMODULUS	8
 #define	LAPB_EMODULUS	128
 
+/*
+ *	Information about the current frame.
+ */
+struct lapb_frame {
+	unsigned short		type;		/* Parsed type		*/
+	unsigned short		nr, ns;		/* N(R), N(S)		*/
+	unsigned char		cr;		/* Command/Response	*/
+	unsigned char		pf;		/* Poll/Final		*/
+	unsigned char		control[2];	/* Original control data*/
+};
+
+/*
+ *	The per LAPB connection control structure.
+ */
 typedef struct lapb_cb {
 	struct lapb_cb		*next;
 	void			*token;
+
+	/* Link status fields */
 	unsigned int		mode;
 	unsigned char		state;
 	unsigned short		vs, vr, va;
@@ -69,12 +92,18 @@
 	unsigned short		n2, n2count;
 	unsigned short		t1, t2;
 	unsigned short		t1timer, t2timer;
+
+	/* Internal control information */
 	struct sk_buff_head	input_queue;
 	struct sk_buff_head	write_queue;
 	struct sk_buff_head	ack_queue;
 	unsigned char		window;
 	struct timer_list	timer;
 	struct lapb_register_struct callbacks;
+
+	/* FRMR control information */
+	struct lapb_frame	frmr_data;
+	unsigned char		frmr_type;
 } lapb_cb;
 
 /* lapb_iface.c */
@@ -102,8 +131,9 @@
 extern void lapb_frames_acked(lapb_cb *, unsigned short);
 extern void lapb_requeue_frames(lapb_cb *);
 extern int  lapb_validate_nr(lapb_cb *, unsigned short);
-extern int  lapb_decode(lapb_cb *, struct sk_buff *, int *, int *, int *, int *);
+extern void lapb_decode(lapb_cb *, struct sk_buff *, struct lapb_frame *);
 extern void lapb_send_control(lapb_cb *, int, int, int);
+extern void lapb_transmit_frmr(lapb_cb *);
 
 /* lapb_timer.c */
 extern void lapb_set_timer(lapb_cb *);
diff -u --recursive --new-file v2.1.26/linux/include/net/netrom.h linux/include/net/netrom.h
--- v2.1.26/linux/include/net/netrom.h	Sun Feb  2 05:18:47 1997
+++ linux/include/net/netrom.h	Fri Feb 21 14:58:36 1997
@@ -28,11 +28,12 @@
 #define	NR_MORE_FLAG			0x20
 
 /* Define Link State constants. */
-
-#define NR_STATE_0			0
-#define NR_STATE_1			1
-#define NR_STATE_2			2
-#define NR_STATE_3			3
+enum {
+	NR_STATE_0,
+	NR_STATE_1,
+	NR_STATE_2,
+	NR_STATE_3
+};
 
 #define	NR_COND_ACK_PENDING		0x01
 #define	NR_COND_REJECT			0x02
diff -u --recursive --new-file v2.1.26/linux/include/net/rose.h linux/include/net/rose.h
--- v2.1.26/linux/include/net/rose.h	Sun Feb  2 05:18:47 1997
+++ linux/include/net/rose.h	Fri Feb 21 14:58:36 1997
@@ -40,11 +40,13 @@
 
 /* Define Link State constants. */
 
-#define ROSE_STATE_0			0		/* Ready */
-#define ROSE_STATE_1			1		/* Awaiting Call Accepted */
-#define ROSE_STATE_2			2		/* Awaiting Clear Confirmation */
-#define ROSE_STATE_3			3		/* Data Transfer */
-#define	ROSE_STATE_4			4		/* Awaiting Reset Confirmation */
+enum {
+	ROSE_STATE_0,			/* Ready */
+	ROSE_STATE_1,			/* Awaiting Call Accepted */
+	ROSE_STATE_2,			/* Awaiting Clear Confirmation */
+	ROSE_STATE_3,			/* Data Transfer */
+	ROSE_STATE_4			/* Awaiting Reset Confirmation */
+};
 
 #define ROSE_DEFAULT_T0			(180 * ROSE_SLOWHZ)	/* Default T10 T20 value */
 #define ROSE_DEFAULT_T1			(200 * ROSE_SLOWHZ)	/* Default T11 T21 value */
diff -u --recursive --new-file v2.1.26/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.26/linux/include/net/sock.h	Thu Feb  6 02:55:48 1997
+++ linux/include/net/sock.h	Wed Feb 26 14:08:33 1997
@@ -700,7 +700,11 @@
 						     int noblock,
 						     int *errcode);
 extern int 			sock_no_fcntl(struct socket *, unsigned int, unsigned long);
-
+extern int			sock_no_getsockopt(struct socket *, int , int,
+						   char *, int *);
+extern int			sock_no_setsockopt(struct socket *, int, int,
+						   char *, int);
+extern int			sock_no_listen(struct socket *, int);
 /*
  *	Default socket callbacks and setup code
  */
diff -u --recursive --new-file v2.1.26/linux/include/net/x25.h linux/include/net/x25.h
--- v2.1.26/linux/include/net/x25.h	Sun Jan 19 05:47:27 1997
+++ linux/include/net/x25.h	Fri Feb 21 14:58:36 1997
@@ -52,17 +52,20 @@
 #define	X25_COND_PEER_RX_BUSY	0x04
 
 /* Define Link State constants. */
+enum {
+	X25_STATE_0,		/* Ready */
+	X25_STATE_1,		/* Awaiting Call Accepted */
+	X25_STATE_2,		/* Awaiting Clear Confirmation */
+	X25_STATE_3,		/* Data Transfer */
+	X25_STATE_4		/* Awaiting Reset Confirmation */
+};
 
-#define X25_STATE_0		0		/* Ready */
-#define X25_STATE_1		1		/* Awaiting Call Accepted */
-#define X25_STATE_2		2		/* Awaiting Clear Confirmation */
-#define X25_STATE_3		3		/* Data Transfer */
-#define	X25_STATE_4		4		/* Awaiting Reset Confirmation */
-
-#define	X25_LINK_STATE_0	0
-#define	X25_LINK_STATE_1	1
-#define	X25_LINK_STATE_2	2
-#define	X25_LINK_STATE_3	3
+enum {
+	X25_LINK_STATE_0,
+	X25_LINK_STATE_1,
+	X25_LINK_STATE_2,
+	X25_LINK_STATE_3
+};
 
 #define X25_DEFAULT_T20		(180 * X25_SLOWHZ)	/* Default T20 value */
 #define X25_DEFAULT_T21		(200 * X25_SLOWHZ)	/* Default T21 value */
diff -u --recursive --new-file v2.1.26/linux/init/main.c linux/init/main.c
--- v2.1.26/linux/init/main.c	Sun Feb  2 05:53:10 1997
+++ linux/init/main.c	Tue Feb 25 17:12:50 1997
@@ -166,6 +166,12 @@
 #ifdef CONFIG_ISDN_DRV_TELES
 extern void teles_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_ISDN_DRV_HISAX
+extern void HiSax_setup(char *str, int *ints);
+#endif
+#ifdef CONFIG_ISDN_DRV_PCBIT
+extern void pcbit_setup(char *str, int *ints);
+#endif
 
 #ifdef CONFIG_ATARIMOUSE
 extern void atari_mouse_setup (char *str, int *ints);
@@ -182,9 +188,6 @@
 #ifdef CONFIG_DIGI
 extern void pcxx_setup(char *str, int *ints);
 #endif
-#ifdef CONFIG_ISDN_DRV_PCBIT
-extern void pcbit_setup(char *str, int *ints);
-#endif
 #ifdef CONFIG_RISCOM8
 extern void riscom8_setup(char *str, int *ints);
 #endif
@@ -437,6 +440,10 @@
 #endif
 #ifdef CONFIG_ISDN_DRV_TELES
 	{ "teles=", teles_setup },
+#endif
+#ifdef CONFIG_ISDN_DRV_HISAX
+       { "hisax=", HiSax_setup },
+       { "HiSax=", HiSax_setup },
 #endif
 #ifdef CONFIG_ISDN_DRV_PCBIT
 	{ "pcbit=", pcbit_setup },
diff -u --recursive --new-file v2.1.26/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.26/linux/kernel/sched.c	Fri Feb  7 05:54:55 1997
+++ linux/kernel/sched.c	Fri Feb 21 14:58:36 1997
@@ -99,7 +99,12 @@
 
 unsigned long volatile jiffies=0;
 
-struct task_struct *current_set[NR_CPUS];
+/*
+ *	Init task must be ok at boot for the ix86 as we will check its signals
+ *	via the SMP irq return path.
+ */
+ 
+struct task_struct *current_set[NR_CPUS] = {&init_task, };
 struct task_struct *last_task_used_math = NULL;
 
 struct task_struct * task[NR_TASKS] = {&init_task, };
diff -u --recursive --new-file v2.1.26/linux/kernel/signal.c linux/kernel/signal.c
--- v2.1.26/linux/kernel/signal.c	Tue Feb  4 06:44:25 1997
+++ linux/kernel/signal.c	Tue Feb 25 13:24:34 1997
@@ -27,42 +27,41 @@
  * This call isn't used by all ports, in particular, the Alpha
  * uses osf_sigprocmask instead.  Maybe it should be moved into
  * arch-dependent dir?
+ *
+ * We don't need to get the kernel lock - this is all local to this
+ * particular thread.. (and that's good, because this is _heavily_
+ * used by various programs)
  */
 asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset)
 {
-	sigset_t new_set, old_set = current->blocked;
-	int error;
+	sigset_t old_set = current->blocked;
 
-	lock_kernel();
 	if (set) {
-		error = get_user(new_set, set);
+		sigset_t new_set;
+		int error = get_user(new_set, set);
 		if (error)
-			goto out;
+			return error;
 		new_set &= _BLOCKABLE;
 		switch (how) {
+		default:
+			return -EINVAL;
 		case SIG_BLOCK:
-			current->blocked |= new_set;
+			new_set |= old_set;
 			break;
 		case SIG_UNBLOCK:
-			current->blocked &= ~new_set;
+			new_set = old_set & ~new_set;
 			break;
 		case SIG_SETMASK:
-			current->blocked = new_set;
 			break;
-		default:
-			error = -EINVAL;
-			goto out;
 		}
+		current->blocked = new_set;
 	}
 	if (oset) {
-		error = put_user(old_set, oset);
+		int error = put_user(old_set, oset);
 		if (error)
-			goto out;
+			return error;
 	}
-	error = 0;
-out:
-	unlock_kernel();
-	return error;
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/kernel/softirq.c linux/kernel/softirq.c
--- v2.1.26/linux/kernel/softirq.c	Sun Jan 26 02:07:49 1997
+++ linux/kernel/softirq.c	Wed Feb 26 14:05:16 1997
@@ -22,8 +22,9 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/bitops.h>
+#include <asm/atomic.h>
 
-unsigned long intr_count = 0;
+atomic_t intr_count = 0;
 
 int bh_mask_count[32];
 unsigned long bh_active = 0;
@@ -31,32 +32,52 @@
 void (*bh_base[32])(void);
 
 
-asmlinkage void do_bottom_half(void)
+/*
+ * This needs to make sure that only one bottom half handler
+ * is ever active at a time. We do this without locking by
+ * doing an atomic increment on the intr_count, and checking
+ * (nonatomically) against 1. Only if it's 1 do we schedule
+ * the bottom half.
+ *
+ * Note that the non-atomicity of the test (as opposed to the
+ * actual update) means that the test may fail, and _nobody_
+ * runs the handlers if there is a race that makes multiple
+ * CPU's get here at the same time. That's ok, we'll run them
+ * next time around.
+ */
+static inline void run_bottom_halves(void)
 {
 	unsigned long active;
 	unsigned long mask, left;
 	void (**bh)(void);
 
-	lock_kernel();
-	intr_count=1;
+	cli();
+	active = bh_active & bh_mask;
+	bh_active &= ~active;
 	sti();
 	bh = bh_base;
-	active = bh_active & bh_mask;
 	for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) {
 		if (mask & active) {
-			void (*fn)(void);
-			bh_active &= ~mask;
-			fn = *bh;
-			if (!fn)
-				goto bad_bh;
-			fn();
+			(*bh)();
 		}
 	}
-	goto out;
-bad_bh:
-	printk ("irq.c:bad bottom half entry %08lx\n", mask);
-out:
-	intr_count=0;
+}
+
+/*
+ * We really shouldn't need to get the kernel lock here,
+ * but we do it the easy way for now (the scheduler gets
+ * upset if somebody messes with intr_count without having
+ * the kernel lock).
+ *
+ * Get rid of the kernel lock here at the same time we
+ * make interrupt handling sane. 
+ */
+asmlinkage void do_bottom_half(void)
+{
+	lock_kernel();
+	atomic_inc(&intr_count);
+	if (intr_count == 1)
+		run_bottom_halves();
+	atomic_dec(&intr_count);
 	unlock_kernel();
-	return;
 }
diff -u --recursive --new-file v2.1.26/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.26/linux/kernel/sysctl.c	Sun Jan 26 02:07:49 1997
+++ linux/kernel/sysctl.c	Tue Feb 25 17:12:51 1997
@@ -102,6 +102,7 @@
 				    void *, size_t, void **);
 
 extern char binfmt_java_interpreter[], binfmt_java_appletviewer[];
+extern int sysctl_overcommit_memory;
 
 #ifdef __sparc__
 extern char reboot_command [];
@@ -172,6 +173,8 @@
 	{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
 	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
 	 &bdflush_min, &bdflush_max},
+	{VM_OVERCOMMIT_MEMORY, "overcommit_memory", &sysctl_overcommit_memory,
+	 sizeof(sysctl_overcommit_memory), 0644, NULL, &proc_dointvec},
 	{0}
 };
 
diff -u --recursive --new-file v2.1.26/linux/mm/mmap.c linux/mm/mmap.c
--- v2.1.26/linux/mm/mmap.c	Sun Jan 26 02:07:49 1997
+++ linux/mm/mmap.c	Tue Feb 25 17:12:51 1997
@@ -46,6 +46,8 @@
 /* SLAB cache for vm_area_struct's. */
 kmem_cache_t *vm_area_cachep;
 
+int sysctl_overcommit_memory;
+
 /*
  * Check that a process has enough memory to allocate a
  * new virtual mapping.
@@ -58,6 +60,11 @@
 	 * fool it, but this should catch most mistakes.
 	 */
 	long freepages;
+	
+        /* sometimes we want to use more memory than we have. */
+	if (sysctl_overcommit_memory)
+	    return 1;
+
 	freepages = buffermem >> PAGE_SHIFT;
 	freepages += page_cache_size;
 	freepages >>= 1;
diff -u --recursive --new-file v2.1.26/linux/net/802/cl2llc.c linux/net/802/cl2llc.c
--- v2.1.26/linux/net/802/cl2llc.c	Thu Jan  2 05:13:27 1997
+++ linux/net/802/cl2llc.c	Tue Feb 25 17:12:51 1997
@@ -21,6 +21,7 @@
  *	Changes
  *		Alan Cox	:	Chainsawed into Linux format
  *					Modified to use llc_ names
+ *					Changed callbacks
  *
  *	This file must be processed by sed before it can be compiled.
  */
diff -u --recursive --new-file v2.1.26/linux/net/802/cl2llc.pre linux/net/802/cl2llc.pre
--- v2.1.26/linux/net/802/cl2llc.pre	Thu Jan  2 05:13:27 1997
+++ linux/net/802/cl2llc.pre	Tue Feb 25 17:12:51 1997
@@ -23,9 +23,6 @@
  *					Modified to use llc_ names
  *					Changed callbacks
  *
- *			Note: TST/XID stuff is broken at the moment. The
- *			buffer is freed before being passed up.
- *
  *	This file must be processed by sed before it can be compiled.
  */
 
diff -u --recursive --new-file v2.1.26/linux/net/802/fddi.c linux/net/802/fddi.c
--- v2.1.26/linux/net/802/fddi.c	Tue Feb  4 06:44:25 1997
+++ linux/net/802/fddi.c	Fri Feb 21 14:58:37 1997
@@ -37,6 +37,7 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/fddidevice.h>
+#include <linux/if_ether.h>
 #include <linux/skbuff.h>
 #include <linux/errno.h>
 #include <net/arp.h>
diff -u --recursive --new-file v2.1.26/linux/net/802/llc_macinit.c linux/net/802/llc_macinit.c
--- v2.1.26/linux/net/802/llc_macinit.c	Sun Jan 19 05:47:27 1997
+++ linux/net/802/llc_macinit.c	Tue Feb 25 17:12:51 1997
@@ -51,6 +51,8 @@
 	frameptr fr;
 	int free=1;
 
+	lp->inc_skb=NULL;
+	
 	/*
 	 *	Truncate buffer to true 802.3 length
 	 *	[FIXME: move to 802.2 demux]
@@ -87,6 +89,7 @@
 			case TEST_RSP:
 				lp->llc_callbacks|=LLC_TEST_INDICATION;
 				lp->inc_skb=skb;
+				free=0;
 				break;
 			case XID_CMD:
 				/*
@@ -110,12 +113,14 @@
 				}
 				lp->llc_callbacks|=LLC_XID_INDICATION;
 				lp->inc_skb=skb;
+				free=0;
 				break;
 
 			case UI_CMD:
 				lp->llc_callbacks|=LLC_UI_DATA;
 				skb_pull(skb,3);
 				lp->inc_skb=skb;
+				free=0;
 				break;
 
 			default:
diff -u --recursive --new-file v2.1.26/linux/net/Config.in linux/net/Config.in
--- v2.1.26/linux/net/Config.in	Sun Feb  2 05:18:48 1997
+++ linux/net/Config.in	Tue Feb 25 17:12:51 1997
@@ -31,6 +31,8 @@
 fi
 tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
 if [ "$CONFIG_AX25" != "n" ]; then
+# bool 'AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+# bool 'AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
   dep_tristate 'Amateur Radio NET/ROM' CONFIG_NETROM $CONFIG_AX25
   dep_tristate 'Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
 fi
diff -u --recursive --new-file v2.1.26/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.26/linux/net/appletalk/ddp.c	Sun Feb  2 05:18:48 1997
+++ linux/net/appletalk/ddp.c	Tue Feb 25 17:12:51 1997
@@ -23,6 +23,7 @@
  *		Alan Cox		:	Hooks for PPP (based on the
  *						localtalk hook).
  *		Alan Cox		:	Posix bits
+ *		Alan Cox/Mike Freeman	:	Possible fix to NBP problems
  *		Bradford Johnson	:	IP-over-DDP (experimental)
  *
  *		This program is free software; you can redistribute it and/or
@@ -126,7 +127,8 @@
 
 	    	if ( to->sat_addr.s_net == s->protinfo.af_at.src_net &&
 		    (to->sat_addr.s_node == s->protinfo.af_at.src_node
-		     ||to->sat_addr.s_node == ATADDR_BCAST ))
+		     ||to->sat_addr.s_node == ATADDR_BCAST
+			||to->sat_addr.s_node == ATADDR_ANYNODE ))
 		{
 			break;
 	   	}
@@ -428,8 +430,10 @@
 	struct atalk_iface *iface;
 	for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
 	{
-		if((node==ATADDR_BCAST || iface->address.s_node==node)
-			&& iface->address.s_net==net && !(iface->status&ATIF_PROBE))
+		if((node==ATADDR_BCAST || node==ATADDR_ANYNODE 
+			|| iface->address.s_node==node)
+			&& iface->address.s_net==net 
+			&& !(iface->status&ATIF_PROBE))
 			return iface;
 	}
 	return NULL;
@@ -916,36 +920,6 @@
 }
 
 /*
- *	Set 'magic' options for appletalk. If we don't have any this is fine
- *	as it is.
- */
-
-static int atalk_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
-{
-	return -EOPNOTSUPP;
-}
-
-
-/*
- *	Get any magic options. Comment above applies.
- */
-
-static int atalk_getsockopt(struct socket *sock, int level, int optname,
-	char *optval, int *optlen)
-{
-	return -ENOPROTOOPT;
-}
-
-/*
- *	Only for connection oriented sockets - ignore
- */
-
-static int atalk_listen(struct socket *sock, int backlog)
-{
-	return -EOPNOTSUPP;
-}
-
-/*
  *	Create a socket. Initialise the socket, blank the addresses
  *	set the state.
  */
@@ -2004,10 +1978,10 @@
 	atalk_getname,
 	datagram_poll,
 	atalk_ioctl,
-	atalk_listen,
+	sock_no_listen,
 	atalk_shutdown,
-	atalk_setsockopt,
-	atalk_getsockopt,
+	sock_no_setsockopt,
+	sock_no_getsockopt,
 	sock_no_fcntl,
 	atalk_sendmsg,
 	atalk_recvmsg
diff -u --recursive --new-file v2.1.26/linux/net/ax25/Makefile linux/net/ax25/Makefile
--- v2.1.26/linux/net/ax25/Makefile	Sun Nov 10 09:12:57 1996
+++ linux/net/ax25/Makefile	Tue Feb 25 17:12:51 1997
@@ -9,7 +9,9 @@
 
 
 O_TARGET := ax25.o
-O_OBJS	 := sysctl_net_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
+O_OBJS	 := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \
+	    ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \
+	    ax25_subr.o ax25_timer.o ax25_uid.o sysctl_net_ax25.o
 M_OBJS   := $(O_TARGET)
 
 OX_OBJS  += af_ax25.o
diff -u --recursive --new-file v2.1.26/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.26/linux/net/ax25/af_ax25.c	Sun Feb  2 05:18:48 1997
+++ linux/net/ax25/af_ax25.c	Tue Feb 25 17:12:51 1997
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 035
+ *	AX.25 release 036
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
  *	releases, misbehave and/or generally screw up. It might even work.
@@ -92,11 +92,7 @@
  *			Alan(GW4PTS)		Added asynchronous support.
  *			Frederic(F1OAT)		Support for pseudo-digipeating.
  *			Jonathan(G4KLX)		Support for packet forwarding.
- *
- *	To do:
- *		Restructure the ax25_rcv code to be cleaner/faster and
- *		copy only when needed.
- *		Consider better arbitrary protocol support.
+ *	AX.25 036	Jonathan(G4KLX)		Major restructuring.
  */
 
 #include <linux/config.h>
@@ -132,128 +128,15 @@
 #include <net/ip.h>
 #include <net/arp.h>
 
-/*
- *	The null address is defined as a callsign of all spaces with an
- *	SSID of zero.
- */
-ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
-
 ax25_cb *volatile ax25_list = NULL;
 
 static struct proto_ops ax25_proto_ops;
 
 /*
- *	ax25 -> ascii conversion
- */
-char *ax2asc(ax25_address *a)
-{
-	static char buf[11];
-	char c, *s;
-	int n;
-
-	for (n = 0, s = buf; n < 6; n++) {
-		c = (a->ax25_call[n] >> 1) & 0x7F;
-
-		if (c != ' ') *s++ = c;
-	}
-
-	*s++ = '-';
-
-	if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
-		*s++ = '1';
-		n -= 10;
-	}
-
-	*s++ = n + '0';
-	*s++ = '\0';
-
-	if (*buf == '\0' || *buf == '-')
-	   return "*";
-
-	return buf;
-
-}
-
-/*
- *	ascii -> ax25 conversion
- */
-ax25_address *asc2ax(char *callsign)
-{
-	static ax25_address addr;
-	char *s;
-	int n;
-
-	for (s = callsign, n = 0; n < 6; n++) {
-		if (*s != '\0' && *s != '-')
-			addr.ax25_call[n] = *s++;
-		else
-			addr.ax25_call[n] = ' ';
-		addr.ax25_call[n] <<= 1;
-		addr.ax25_call[n] &= 0xFE;
-	}
-
-	if (*s++ == '\0') {
-		addr.ax25_call[6] = 0x00;
-		return &addr;
-	}
-
-	addr.ax25_call[6] = *s++ - '0';
-
-	if (*s != '\0') {
-		addr.ax25_call[6] *= 10;
-		addr.ax25_call[6] += *s++ - '0';
-	}
-
-	addr.ax25_call[6] <<= 1;
-	addr.ax25_call[6] &= 0x1E;
-
-	return &addr;
-}
-
-/*
- *	Compare two ax.25 addresses
- */
-int ax25cmp(ax25_address *a, ax25_address *b)
-{
-	int ct = 0;
-
-	while (ct < 6) {
-		if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE))	/* Clean off repeater bits */
-			return 1;
-		ct++;
-	}
-
- 	if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E))	/* SSID without control bit */
- 		return 0;
-
- 	return 2;			/* Partial match */
-}
-
-/*
- *	Compare two AX.25 digipeater paths.
- */
-static int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2)
-{
-	int i;
-
-	if (digi1->ndigi != digi2->ndigi)
-		return 1;
-
-	if (digi1->lastrepeat != digi2->lastrepeat)
-		return 1;
-
-	for (i = 0; i < digi1->ndigi; i++)
-		if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0)
-			return 1;
-
-	return 0;
-}
-
-/*
  *	Free an allocated ax25 control block. This is done to centralise
  *	the MOD count code.
  */
-static void ax25_free_cb(ax25_cb *ax25)
+void ax25_free_cb(ax25_cb *ax25)
 {
 	if (ax25->digipeat != NULL) {
 		kfree_s(ax25->digipeat, sizeof(ax25_digi));
@@ -300,12 +183,16 @@
  */
 static void ax25_kill_by_device(struct device *dev)
 {
+	ax25_dev *ax25_dev;
 	ax25_cb *s;
 
+	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+		return;
+
 	for (s = ax25_list; s != NULL; s = s->next) {
-		if (s->device == dev) {
-			s->state  = AX25_STATE_0;
-			s->device = NULL;
+		if (s->ax25_dev == ax25_dev) {
+			s->state    = AX25_STATE_0;
+			s->ax25_dev = NULL;
 			if (s->sk != NULL) {
 				s->sk->state     = TCP_CLOSE;
 				s->sk->err       = ENETUNREACH;
@@ -348,7 +235,7 @@
 /*
  *	Add a socket to the bound sockets list.
  */
-static void ax25_insert_socket(ax25_cb *ax25)
+void ax25_insert_socket(ax25_cb *ax25)
 {
 	unsigned long flags;
 
@@ -365,7 +252,7 @@
  *	Find a socket that wants to accept the SABM we have just
  *	received.
  */
-static struct sock *ax25_find_listener(ax25_address *addr, int digi, struct device *dev, int type)
+struct sock *ax25_find_listener(ax25_address *addr, int digi, struct device *dev, int type)
 {
 	unsigned long flags;
 	ax25_cb *s;
@@ -378,7 +265,7 @@
 			continue;
 		if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) {
 			/* If device is null we match any device */
-			if (s->device == NULL || s->device == dev) {
+			if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
 				restore_flags(flags);
 				return s->sk;
 			}
@@ -392,7 +279,7 @@
 /*
  *	Find an AX.25 socket given both ends.
  */
-static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type)
+struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type)
 {
 	ax25_cb *s;
 	unsigned long flags;
@@ -416,7 +303,7 @@
  *	Find an AX.25 control block given both ends. It will only pick up
  *	floating AX.25 control blocks or non Raw socket bound control blocks.
  */
-static ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev)
+ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev)
 {
 	ax25_cb *s;
 	unsigned long flags;
@@ -427,7 +314,7 @@
 	for (s = ax25_list; s != NULL; s = s->next) {
 		if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
 			continue;
-		if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->device == dev) {
+		if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
 			if (digi != NULL) {
 				if (s->digipeat == NULL && digi->ndigi != 0)
 					continue;
@@ -447,7 +334,7 @@
 /*
  *	Look for any matching address - RAW sockets can bind to arbitrary names
  */
-static struct sock *ax25_addr_match(ax25_address *addr)
+struct sock *ax25_addr_match(ax25_address *addr)
 {
 	unsigned long flags;
 	ax25_cb *s;
@@ -467,7 +354,7 @@
 	return NULL;
 }
 
-static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
+void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
 {
 	struct sk_buff *copy;
 
@@ -529,7 +416,7 @@
 	}
 
 	if (ax25->sk != NULL) {
-		if (ax25->sk->wmem_alloc > 0 || ax25->sk->rmem_alloc > 0) {	/* Defer: outstanding buffers */
+		if (ax25->sk->wmem_alloc != 0 || ax25->sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
 			init_timer(&ax25->timer);
 			ax25->timer.expires  = jiffies + 10 * HZ;
 			ax25->timer.function = ax25_destroy_timer;
@@ -547,80 +434,6 @@
 }
 
 /*
- *	Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
- */
-
-ax25_uid_assoc *ax25_uid_list;
-
-int ax25_uid_policy = 0;
-
-ax25_address *ax25_findbyuid(uid_t uid)
-{
-	ax25_uid_assoc *a;
-
-	for (a = ax25_uid_list; a != NULL; a = a->next) {
-		if (a->uid == uid)
-			return &a->call;
-	}
-
-	return NULL;
-}
-
-static int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
-{
-	ax25_uid_assoc *a;
-
-	switch (cmd) {
-		case SIOCAX25GETUID:
-			for (a = ax25_uid_list; a != NULL; a = a->next) {
-				if (ax25cmp(&sax->sax25_call, &a->call) == 0)
-					return a->uid;
-			}
-			return -ENOENT;
-
-		case SIOCAX25ADDUID:
-			if (!suser())
-				return -EPERM;
-			if (ax25_findbyuid(sax->sax25_uid))
-				return -EEXIST;
-			if (sax->sax25_uid == 0)
-				return -EINVAL;
-			a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL);
-			if (a == NULL)
-				return -ENOMEM;
-			a->uid  = sax->sax25_uid;
-			a->call = sax->sax25_call;
-			a->next = ax25_uid_list;
-			ax25_uid_list = a;
-			return 0;
-
-		case SIOCAX25DELUID: {
-			ax25_uid_assoc **l;
-
-			if (!suser())
-				return -EPERM;
-			l = &ax25_uid_list;
-			while ((*l) != NULL) {
-				if (ax25cmp(&((*l)->call), &(sax->sax25_call)) == 0) {
-					a = *l;
-					*l = (*l)->next;
-					kfree_s(a, sizeof(*a));
-					return 0;
-				}
-
-				l = &((*l)->next);
-			}
-			return -ENOENT;
-		}
-
-		default:
-			return -EINVAL;
-	}
-
-	return -EINVAL;	/*NOTREACHED */
-}
-
-/*
  * dl1bke 960311: set parameters for existing AX.25 connections,
  *		  includes a KILL command to abort any connection.
  *		  VERY useful for debugging ;-)
@@ -628,7 +441,7 @@
 static int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
 {
 	struct ax25_ctl_struct ax25_ctl;
-	struct device *dev;
+	ax25_dev *ax25_dev;
 	ax25_cb *ax25;
 	unsigned long flags;
 	int err;
@@ -638,10 +451,10 @@
 
 	copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl));
 
-	if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL)
+	if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL)
 		return -ENODEV;
 
-	if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, NULL, dev)) == NULL)
+	if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, NULL, ax25_dev->dev)) == NULL)
 		return -ENOTCONN;
 
 	switch (ax25_ctl.cmd) {
@@ -659,7 +472,10 @@
 				ax25->sk->dead  = 1;
 			}
 
-			ax25_dama_off(ax25);
+#ifdef CONFIG_AX25_DAMA_SLAVE
+			if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
+				ax25_dama_off(ax25);
+#endif
 			ax25_set_timer(ax25);
 	  		break;
 
@@ -725,19 +541,9 @@
 	  	case AX25_PACLEN:
 	  		if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
 	  			return -EINVAL;
-#if 0
-	  		if (ax25_ctl.arg > 256) /* we probably want this */
-	  			printk(KERN_WARNING "ax25_ctl_ioctl: Warning --- huge paclen %d\n", (int)ax25_ctl.arg);
-#endif
 	  		ax25->paclen = ax25_ctl.arg;
 	  		break;
 
-	  	case AX25_MAXQUEUE:
-	  		if (ax25_ctl.arg < 1)
-	  			return -EINVAL;
-	  		ax25->maxqueue = ax25_ctl.arg;
-	  		break;
-
 	  	default:
 	  		return -EINVAL;
 	  }
@@ -748,11 +554,11 @@
 /*
  * Create an empty AX.25 control block.
  */
-static ax25_cb *ax25_create_cb(void)
+ax25_cb *ax25_create_cb(void)
 {
 	ax25_cb *ax25;
 
-	if ((ax25 = (ax25_cb *)kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
+	if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	MOD_INC_USE_COUNT;
@@ -772,7 +578,6 @@
 	ax25->t3      = AX25_DEF_T3;
 	ax25->n2      = AX25_DEF_N2;
 	ax25->paclen  = AX25_DEF_PACLEN;
-	ax25->maxqueue= AX25_DEF_MAXQUEUE;
 	ax25->idle    = AX25_DEF_IDLE;
 
 	if (AX25_DEF_AXDEFMODE) {
@@ -790,150 +595,30 @@
 }
 
 /*
- *	Find out if we are a DAMA slave for this device and count the
- *	number of connections.
- *
- *	dl1bke 951121
- */
-int ax25_dev_is_dama_slave(struct device *dev)
-{
-	ax25_cb *ax25;
-	int count = 0;
-
-	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
-		if (ax25->device == dev && ax25->dama_slave) {
-			count++;
-			break;
-		}
-	}
-
-	return count;
-}
-
-/*
  *	Fill in a created AX.25 created control block with the default
  *	values for a particular device.
  */
-static void ax25_fillin_cb(ax25_cb *ax25, struct device *dev)
+void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev)
 {
-	ax25->device  = dev;
+	ax25->ax25_dev = ax25_dev;
 
-	ax25->rtt      = ax25_dev_get_value(dev, AX25_VALUES_T1);
-	ax25->t1       = ax25_dev_get_value(dev, AX25_VALUES_T1);
-	ax25->t2       = ax25_dev_get_value(dev, AX25_VALUES_T2);
-	ax25->t3       = ax25_dev_get_value(dev, AX25_VALUES_T3);
-	ax25->n2       = ax25_dev_get_value(dev, AX25_VALUES_N2);
-	ax25->paclen   = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
-	ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_MAXQUEUE);
-	ax25->idle     = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
+	ax25->rtt      = ax25_dev->values[AX25_VALUES_T1];
+	ax25->t1       = ax25_dev->values[AX25_VALUES_T1];
+	ax25->t2       = ax25_dev->values[AX25_VALUES_T2];
+	ax25->t3       = ax25_dev->values[AX25_VALUES_T3];
+	ax25->n2       = ax25_dev->values[AX25_VALUES_N2];
+	ax25->paclen   = ax25_dev->values[AX25_VALUES_PACLEN];
+	ax25->idle     = ax25_dev->values[AX25_VALUES_IDLE];
 
-	if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) {
+	if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) {
 		ax25->modulus = AX25_EMODULUS;
-		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+		ax25->window  = ax25_dev->values[AX25_VALUES_EWINDOW];
 	} else {
 		ax25->modulus = AX25_MODULUS;
-		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
-	}
-
-	ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF);
-}
-
-int ax25_send_frame(struct sk_buff *skb, int fragment, ax25_address *src, ax25_address *dest,
-	ax25_digi *digi, struct device *dev)
-{
-	ax25_cb *ax25;
-
-	if (skb == NULL)
-		return 0;
-
-	/*
-	 * Look for an existing connection.
-	 */
-	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
-		if (ax25->sk != NULL && ax25->sk->type != SOCK_SEQPACKET)
-			continue;
-
-		if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev) {
-			if (ax25_queue_length(ax25, skb) > ax25->maxqueue * ax25->window) {
-				kfree_skb(skb, FREE_WRITE);
-			} else {
-				ax25_output(ax25, fragment, skb);
-			}
-			ax25->idletimer = ax25->idle;
-			return 1;		/* It already existed */
-		}
-	}
-
-	if ((ax25 = ax25_create_cb()) == NULL)
-		return 0;
-
-	ax25_fillin_cb(ax25, dev);
-
-	ax25->source_addr = *src;
-	ax25->dest_addr   = *dest;
-
-	if (digi != NULL) {
-		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-			ax25_free_cb(ax25);
-			return 0;
-		}
-		*ax25->digipeat = *digi;
-	} else {
-		ax25_rt_build_path(ax25, dest, dev);
-	}
-
-	if (ax25_dev_is_dama_slave(ax25->device))
-		dama_establish_data_link(ax25);
-	else
-		ax25_establish_data_link(ax25);
-
-	/* idle timeouts only for mode vc connections */
-
-	ax25->idletimer = ax25->idle;
-
-	ax25_insert_socket(ax25);
-
-	ax25->state = AX25_STATE_1;
-
-	ax25_set_timer(ax25);
-
-	ax25_output(ax25, fragment, skb);
-
-	return 1;			/* We had to create it */
-}
-
-/*
- *	Return the state of an AX.25 link given source, destination, and
- *	device.
- */
-int ax25_link_up(ax25_address *src, ax25_address *dest, struct device *dev)
-{
-	ax25_cb *ax25;
-
-	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
-		if (ax25->sk != NULL && ax25->sk->type != SOCK_SEQPACKET)
-			continue;
-
-		if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev)
-			return 1;
+		ax25->window  = ax25_dev->values[AX25_VALUES_WINDOW];
 	}
 
-	return 0;
-}
-
-/*
- *	Find the AX.25 device that matches the hardware address supplied.
- */
-struct device *ax25rtr_get_dev(ax25_address *addr)
-{
-	struct device *dev;
-
-	for (dev = dev_base; dev != NULL; dev = dev->next)
-		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25 &&
-		    ax25cmp(addr, (ax25_address*) dev->dev_addr) == 0)
-		     	return dev;
-
-	return NULL;
+	ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF];
 }
 
 /*
@@ -941,24 +626,22 @@
  *	AX25 socket object
  */
 
-static int ax25_setsockopt(struct socket *sock, int level, int optname,
-	char *optval, int optlen)
+static int ax25_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
 {
 	struct sock *sk = sock->sk;
-	int err, opt;
+	int opt;
 
 	if (level != SOL_AX25)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
 
-	if (optval == NULL)
+	if(optlen<sizeof(int))
 		return -EINVAL;
+		
+	if(get_user(opt, (int *)optval))
+		return -EFAULT;
 
-	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
-		return err;
-
-	get_user(opt, (int *)optval);
-
-	switch (optname) {
+	switch (optname) 
+	{
 		case AX25_WINDOW:
 			if (sk->protinfo.ax25->modulus == AX25_MODULUS) {
 				if (opt < 1 || opt > 7)
@@ -1029,15 +712,18 @@
 	}
 }
 
-static int ax25_getsockopt(struct socket *sock, int level, int optname,
-	char *optval, int *optlen)
+static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
 {
 	struct sock *sk = sock->sk;
 	int val = 0;
 	int err;
+	int len;
 
 	if (level != SOL_AX25)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
+
+	if(get_user(len,optlen))
+		return -EFAULT;
 
 	switch (optname) {
 		case AX25_WINDOW:
@@ -1088,16 +774,11 @@
 			return -ENOPROTOOPT;
 	}
 
-	if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
-		return err;
-
-	put_user(sizeof(int), optlen);
-
-	if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
-		return err;
-
-	put_user(val, (int *)optval);
-
+	len=min(len,sizeof(int));
+	if(put_user(len, optlen))
+		return -EFAULT;
+	if(copy_to_user(optval, &val, len))
+		return -EFAULT;
 	return 0;
 }
 
@@ -1114,7 +795,7 @@
 	return -EOPNOTSUPP;
 }
 
-static int ax25_create(struct socket *sock, int protocol)
+int ax25_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
 	ax25_cb *ax25;
@@ -1182,7 +863,7 @@
 	return 0;
 }
 
-static struct sock *ax25_make_new(struct sock *osk, struct device *dev)
+struct sock *ax25_make_new(struct sock *osk, struct device *dev)
 {
 	struct sock *sk;
 	ax25_cb *ax25;
@@ -1195,8 +876,6 @@
 		return NULL;
 	}
 
-	ax25_fillin_cb(ax25, dev);
-
 	switch (osk->type) {
 		case SOCK_DGRAM:
 			break;
@@ -1233,14 +912,13 @@
 	ax25->n2      = osk->protinfo.ax25->n2;
 	ax25->idle    = osk->protinfo.ax25->idle;
 	ax25->paclen  = osk->protinfo.ax25->paclen;
+	ax25->window  = osk->protinfo.ax25->window;
 
-	ax25->window   = osk->protinfo.ax25->window;
-	ax25->maxqueue = osk->protinfo.ax25->maxqueue;
-
+	ax25->ax25_dev    = osk->protinfo.ax25->ax25_dev;
 	ax25->source_addr = osk->protinfo.ax25->source_addr;
 
 	if (osk->protinfo.ax25->digipeat != NULL) {
-		if ((ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
 			sk_free(sk);
 			ax25_free_cb(ax25);
 			return NULL;
@@ -1282,20 +960,9 @@
 				break;
 
 			case AX25_STATE_1:
-				ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-				sk->protinfo.ax25->state = AX25_STATE_0;
-				sk->state                = TCP_CLOSE;
-				sk->shutdown            |= SEND_SHUTDOWN;
-				sk->state_change(sk);
-				sk->dead                 = 1;
-				ax25_destroy_socket(sk->protinfo.ax25);
-				break;
-
 			case AX25_STATE_2:
-				if (sk->protinfo.ax25->dama_slave)
-					ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-				else
-					ax25_send_control(sk->protinfo.ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+				ax25_clear_queues(sk->protinfo.ax25);
+				ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
 				sk->protinfo.ax25->state = AX25_STATE_0;
 				sk->state                = TCP_CLOSE;
 				sk->shutdown            |= SEND_SHUTDOWN;
@@ -1308,11 +975,16 @@
 			case AX25_STATE_4:
 				ax25_clear_queues(sk->protinfo.ax25);
 				sk->protinfo.ax25->n2count = 0;
-				if (!sk->protinfo.ax25->dama_slave) {
-					ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-					sk->protinfo.ax25->t3timer = 0;
-				} else {
-					sk->protinfo.ax25->t3timer = sk->protinfo.ax25->t3;	/* DAMA slave timeout */
+				switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+					case AX25_PROTO_STD:
+						ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+						sk->protinfo.ax25->t3timer = 0;
+						break;
+#ifdef AX25_CONFIG_DAMA_SLAVE
+					case AX25_PROTO_DAMA_SLAVE:
+						sk->protinfo.ax25->t3timer = sk->protinfo.ax25->t3;	/* DAMA slave timeout */
+						break;
+#endif
 				}
 				sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25);
 				sk->protinfo.ax25->state   = AX25_STATE_2;
@@ -1350,8 +1022,8 @@
 {
 	struct sock *sk = sock->sk;
 	struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
-	struct device *dev;
 	ax25_address *call;
+	ax25_dev *ax25_dev = NULL;
 
 	if (sk->zapped == 0)
 		return -EINVAL;
@@ -1375,24 +1047,26 @@
 
 	if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
 		if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) {
-			dev = NULL;
+			ax25_dev = NULL;
 			SOCK_DEBUG(sk, "AX25: bound to any device\n");
 		} else {
-			if ((dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL) {
+			if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) {
 				SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
 				return -EADDRNOTAVAIL;
 			}
-			SOCK_DEBUG(sk, "AX25: bound to device %s\n", dev->name);
+			SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
 		}
 	} else {
-		if ((dev = ax25rtr_get_dev(&addr->fsa_ax25.sax25_call)) == NULL) {
+		if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) {
 			SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
 			return -EADDRNOTAVAIL;
 		}
-		SOCK_DEBUG(sk, "AX25: bound to device %s\n", dev->name);
+		SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
 	}
 
-	ax25_fillin_cb(sk->protinfo.ax25, dev);
+	if (ax25_dev != NULL)
+		ax25_fillin_cb(sk->protinfo.ax25, ax25_dev);
+
 	ax25_insert_socket(sk->protinfo.ax25);
 
 	sk->zapped = 0;
@@ -1403,8 +1077,7 @@
 /*
  *	FIXME: nonblock behaviour looks like it may have a bug.
  */
-static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
-	int addr_len, int flags)
+static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
@@ -1444,7 +1117,7 @@
 			return -EINVAL;
 
 		if (sk->protinfo.ax25->digipeat == NULL) {
-			if ((sk->protinfo.ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
+			if ((sk->protinfo.ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
 				return -ENOBUFS;
 		}
 
@@ -1471,14 +1144,14 @@
 	if (sk->zapped) {
 		if ((err = ax25_rt_autobind(sk->protinfo.ax25, &addr->sax25_call)) < 0)
 			return err;
-		ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->device);
+		ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
 		ax25_insert_socket(sk->protinfo.ax25);
 	} else {
-		if (sk->protinfo.ax25->device == NULL)
+		if (sk->protinfo.ax25->ax25_dev == NULL)
 			return -EHOSTUNREACH;
 	}
 
-	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, NULL, sk->protinfo.ax25->device) != NULL)
+	if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, NULL, sk->protinfo.ax25->ax25_dev->dev) != NULL)
 		return -EADDRINUSE;			/* Already such a connection */
 
 	sk->protinfo.ax25->dest_addr = addr->sax25_call;
@@ -1494,10 +1167,16 @@
 	sock->state        = SS_CONNECTING;
 	sk->state          = TCP_SYN_SENT;
 
-	if (ax25_dev_is_dama_slave(sk->protinfo.ax25->device))
-		dama_establish_data_link(sk->protinfo.ax25);
-	else
-		ax25_establish_data_link(sk->protinfo.ax25);
+	switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+		case AX25_PROTO_STD:
+			ax25_std_establish_data_link(sk->protinfo.ax25);
+			break;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+		case AX25_PROTO_DAMA_SLAVE:
+			ax25_ds_establish_data_link(sk->protinfo.ax25);
+			break;
+#endif
+	}
 
 	sk->protinfo.ax25->state = AX25_STATE_1;
 	ax25_set_timer(sk->protinfo.ax25);		/* Start going SABM SABM until a UA or a give up and DM */
@@ -1588,8 +1267,7 @@
 	return 0;
 }
 
-static int ax25_getname(struct socket *sock, struct sockaddr *uaddr,
-	int *uaddr_len, int peer)
+static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
 {
 	struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
 	struct sock *sk = sock->sk;
@@ -1616,8 +1294,8 @@
 		sax->fsa_ax25.sax25_ndigis = 1;
 		*uaddr_len = sizeof(struct full_sockaddr_ax25);
 
-		if (sk->protinfo.ax25->device != NULL)
-			memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN);
+		if (sk->protinfo.ax25->ax25_dev != NULL)
+			memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN);
 		else
 			sax->fsa_digipeater[0] = null_ax25_address;
 	}
@@ -1625,324 +1303,7 @@
 	return 0;
 }
 
-static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype)
-{
-	struct sock *make;
-	struct sock *sk;
-	int type = 0;
-	ax25_digi dp, reverse_dp;
-	ax25_cb *ax25;
-	ax25_address src, dest;
-	ax25_address *next_digi = NULL;
-	struct sock *raw;
-	int mine = 0;
-	int dama;
-
-	/*
-	 *	Process the AX.25/LAPB frame.
-	 */
-
-	skb->h.raw = skb->data;
-
-#ifdef CONFIG_FIREWALL
-	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL) != FW_ACCEPT) {
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-#endif
-
-	/*
-	 *	Parse the address header.
-	 */
-
-	if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	/*
-	 *	Ours perhaps ?
-	 */
-	if (dp.lastrepeat + 1 < dp.ndigi) {		/* Not yet digipeated completely */
-		next_digi = &dp.calls[dp.lastrepeat + 1];
-		if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) {
-			struct device *dev_out = dev;
-
-			if ((skb = skb_unshare(skb, GFP_ATOMIC, FREE_READ)) == NULL)
-				return 0;
-
-			/* We are the digipeater. Mark ourselves as repeated
-			   and throw the packet back out of the same device */
-			dp.lastrepeat++;
-			dp.repeated[(int)dp.lastrepeat] = 1;
-
-			if (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) {
-				while (dp.lastrepeat + 1 < dp.ndigi) {
-					struct device *dev_scan;
-					if ((dev_scan = ax25rtr_get_dev(&dp.calls[dp.lastrepeat + 1])) == NULL)
-						break;
-					dp.lastrepeat++;
-					dp.repeated[(int)dp.lastrepeat] = 1;
-					dev_out = dev_scan;
-				}
-				if (dev != dev_out && (ax25_dev_get_value(dev_out, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) == 0) {
-					kfree_skb(skb, FREE_READ);
-					return 0;
-				}
-			}
-
-			if (dev == dev_out && (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_INBAND) == 0) {
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-
-			build_ax25_addr(skb->data, &src, &dest, &dp, type, AX25_MODULUS);
-#ifdef CONFIG_FIREWALL
-			if (call_fw_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-#endif
-			skb->dev      = dev_out;
-			skb->priority = SOPRI_NORMAL;
-
-			ax25_queue_xmit(skb);
-
-			return 0;
-		}
-	}
-
-	/*
-	 *	Pull of the AX.25 headers leaving the CTRL/PID bytes
-	 */
-	skb_pull(skb, size_ax25_addr(&dp));
-
-	/* For our port addresses ? */
-	if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
-		mine = 1;
-
-	/* Also match on any registered callsign from L3/4 */
-	if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
-		mine = 1;
-
-	/* UI frame - bypass LAPB processing */
-	if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
-		skb->h.raw = skb->data + 2;		/* skip control and pid */
-
-		if ((raw = ax25_addr_match(&dest)) != NULL)
-			ax25_send_to_raw(raw, skb, skb->data[1]);
-
-		if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-
-		/* Now we are pointing at the pid byte */
-		switch (skb->data[1]) {
-#ifdef CONFIG_INET
-			case AX25_P_IP:
-				skb_pull(skb,2);		/* drop PID/CTRL */
-				skb->h.raw    = skb->data;
-				skb->nh.raw   = skb->data;
-				skb->dev      = dev;
-				skb->pkt_type = PACKET_HOST;
-				ip_rcv(skb, dev, ptype);	/* Note ptype here is the wrong one, fix me later */
-				break;
-
-			case AX25_P_ARP:
-				skb_pull(skb,2);
-				skb->h.raw    = skb->data;
-				skb->nh.raw   = skb->data;
-				skb->dev      = dev;
-				skb->pkt_type = PACKET_HOST;
-				arp_rcv(skb, dev, ptype);	/* Note ptype here is wrong... */
-				break;
-#endif
-			case AX25_P_TEXT:
-				/* Now find a suitable dgram socket */
-				if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
-					if (sk->rmem_alloc >= sk->rcvbuf) {
-						kfree_skb(skb, FREE_READ);
-					} else {
-						/*
-						 *	Remove the control and PID.
-						 */
-						skb_pull(skb, 2);
-						if (sock_queue_rcv_skb(sk, skb) != 0)
-							kfree_skb(skb, FREE_READ);
-					}
-				} else {
-					kfree_skb(skb, FREE_READ);
-				}
-				break;
-
-			default:
-				kfree_skb(skb, FREE_READ);	/* Will scan SOCK_AX25 RAW sockets */
-				break;
-		}
-
-		return 0;
-	}
-
-	/*
-	 *	Is connected mode supported on this device ?
-	 *	If not, should we DM the incoming frame (except DMs) or
-	 *	silently ignore them. For now we stay quiet.
-	 */
-	if (!ax25_dev_get_value(dev, AX25_VALUES_CONMODE)) {
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	/* LAPB */
-
-	/* AX.25 state 1-4 */
-
-	ax25_digi_invert(&dp, &reverse_dp);
-
-	if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
-		/*
-		 *	Process the frame. If it is queued up internally it returns one otherwise we
-		 *	free it immediately. This routine itself wakes the user context layers so we
-		 *	do no further work
-		 */
-		if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
-			kfree_skb(skb, FREE_READ);
-
-		return 0;
-	}
-
-	/* AX.25 state 0 (disconnected) */
-
-	/* a) received not a SABM(E) */
-
-	if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
-		/*
-		 *	Never reply to a DM. Also ignore any connects for
-		 *	addresses that are not our interfaces and not a socket.
-		 */
-		if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
-			ax25_return_dm(dev, &src, &dest, &dp);
-
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	/* b) received SABM(E) */
-
-	if (dp.lastrepeat + 1 == dp.ndigi)
-		sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
-	else
-		sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
-
-	if (sk != NULL) {
-		if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
-			if (mine)
-				ax25_return_dm(dev, &src, &dest, &dp);
-
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-
-		ax25 = make->protinfo.ax25;
-
-		skb_queue_head(&sk->receive_queue, skb);
-
-		skb->sk     = make;
-		make->state = TCP_ESTABLISHED;
-		make->pair  = sk;
-
-		sk->ack_backlog++;
-	} else {
-		if (!mine) {
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-
-		if ((ax25 = ax25_create_cb()) == NULL) {
-			ax25_return_dm(dev, &src, &dest, &dp);
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-
-		ax25_fillin_cb(ax25, dev);
-		ax25->idletimer = ax25->idle;
-	}
-
-	ax25->source_addr = dest;
-	ax25->dest_addr   = src;
-
-	/*
-	 *	Sort out any digipeated paths.
-	 */
-	if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-		kfree_skb(skb, FREE_READ);
-		ax25_destroy_socket(ax25);
-		return 0;
-	}
-
-	if (dp.ndigi == 0) {
-		if (ax25->digipeat != NULL) {
-			kfree_s(ax25->digipeat, sizeof(ax25_digi));
-			ax25->digipeat = NULL;
-		}
-	} else {
-		/* Reverse the source SABM's path */
-		*ax25->digipeat = reverse_dp;
-	}
-
-	if ((*skb->data & ~AX25_PF) == AX25_SABME) {
-		ax25->modulus = AX25_EMODULUS;
-		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
-	} else {
-		ax25->modulus = AX25_MODULUS;
-		ax25->window  = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
-	}
-
-	ax25->device = dev;
-
-	ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
-
-	if (dama) ax25_dama_on(ax25);	/* bke 951121 */
-
-	ax25->dama_slave = dama;
-	ax25->t3timer = ax25->t3;
-	ax25->state   = AX25_STATE_3;
-
-	ax25_insert_socket(ax25);
-
-	ax25_set_timer(ax25);
-
-	if (sk != NULL) {
-		if (!sk->dead)
-			sk->data_ready(sk, skb->len);
-	} else {
-		kfree_skb(skb, FREE_READ);
-	}
-
-	return 0;
-}
-
-/*
- *	Receive an AX.25 frame via a SLIP interface.
- */
-static int kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
-{
-	skb->sk = NULL;		/* Initially we don't know who it's for */
-
-	if ((*skb->data & 0x0F) != 0) {
-		kfree_skb(skb, FREE_READ);	/* Not a KISS data frame */
-		return 0;
-	}
-
-	skb_pull(skb, AX25_KISS_HEADER_LEN);	/* Remove the KISS byte */
-
-	return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
-}
-
-
-static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len,
-	struct scm_cookie *scm)
+static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
@@ -1967,7 +1328,7 @@
 		return -EPIPE;
 	}
 
-	if (sk->protinfo.ax25->device == NULL)
+	if (sk->protinfo.ax25->ax25_dev == NULL)
 		return -ENETUNREACH;
 
 	if (usax != NULL) {
@@ -2020,7 +1381,7 @@
 	SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n");
 
 	/* Assume the worst case */
-	size = len + 3 + size_ax25_addr(dp) + AX25_BPQ_HEADER_LEN;
+	size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN;
 
 	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
 		return err;
@@ -2045,11 +1406,11 @@
 			return -ENOTCONN;
 		}
 
-		ax25_output(sk->protinfo.ax25, 1, skb);	/* Shove it onto the queue and kick */
+		ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb);	/* Shove it onto the queue and kick */
 
 		return len;
 	} else {
-		asmptr = skb_push(skb, 1 + size_ax25_addr(dp));
+		asmptr = skb_push(skb, 1 + ax25_addr_size(dp));
 
 		SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp);
 
@@ -2057,7 +1418,7 @@
 			SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi);
 
 		/* Build an AX.25 header */
-		asmptr += (lv = build_ax25_addr(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS));
+		asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS));
 
 		SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
 
@@ -2068,7 +1429,7 @@
 		*asmptr = AX25_UI;
 
 		/* Datagram frames go straight out of the door as UI */
-		skb->dev      = sk->protinfo.ax25->device;
+		skb->dev      = sk->protinfo.ax25->ax25_dev->dev;
 		skb->priority = SOPRI_NORMAL;
 
 		ax25_queue_xmit(skb);
@@ -2077,8 +1438,7 @@
 	}
 }
 
-static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags,
-	struct scm_cookie *scm)
+static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
 {
 	struct sock *sk = sock->sk;
 	struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
@@ -2120,7 +1480,7 @@
 		ax25_digi digi;
 		ax25_address dest;
 
-		ax25_parse_addr(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama);
+		ax25_addr_parse(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama);
 
 		sax->sax25_family = AF_AX25;
 		/* We set this correctly, even though we may not let the
@@ -2275,11 +1635,9 @@
 	return 0;
 }
 
-
 static int ax25_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	ax25_cb *ax25;
-	struct device *dev;
 	const char *devname;
 	int len = 0;
 	off_t pos = 0;
@@ -2287,13 +1645,13 @@
 
 	cli();
 
-	len += sprintf(buffer, "dest_addr src_addr  dev  st  vs  vr  va    t1     t2     t3      idle   n2  rtt wnd paclen   dama Snd-Q Rcv-Q\n");
+	len += sprintf(buffer, "dest_addr src_addr  dev  st  vs  vr  va    t1     t2     t3      idle   n2  rtt wnd paclen   Snd-Q Rcv-Q\n");
 
 	for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
-		if ((dev = ax25->device) == NULL)
+		if (ax25->ax25_dev == NULL)
 			devname = "???";
 		else
-			devname = dev->name;
+			devname = ax25->ax25_dev->dev->name;
 
 		len += sprintf(buffer + len, "%-9s ",
 			ax2asc(&ax25->dest_addr));
@@ -2314,8 +1672,6 @@
 			ax25->window,
 			ax25->paclen);
 
-		len += sprintf(buffer + len, " %s", ax25->dama_slave ? " slave" : "    no");
-
 		if (ax25->sk != NULL) {
 			len += sprintf(buffer + len, " %5d %5d\n",
 				ax25->sk->wmem_alloc,
@@ -2379,7 +1735,7 @@
 {
 	0,	/* MUTTER ntohs(ETH_P_AX25),*/
 	0,		/* copy */
-	kiss_rcv,
+	ax25_kiss_rcv,
 	NULL,
 	NULL,
 };
@@ -2423,7 +1779,7 @@
 	PROC_NET_AX25_CALLS, 10, "ax25_calls",
 	S_IFREG | S_IRUGO, 1, 0, 0,
 	0, &proc_net_inode_operations,
-	ax25_cs_get_info
+	ax25_uid_get_info
 };
 #endif
 
@@ -2441,157 +1797,9 @@
 	proc_net_register(&proc_ax25_calls);
 #endif
 
-	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.35 for Linux NET3.038 (Linux 2.1)\n");
-}
-
-/*
- *	A small shim to dev_queue_xmit to add the KISS control byte, and do
- *	any packet forwarding in operation.
- */
-void ax25_queue_xmit(struct sk_buff *skb)
-{
-	unsigned char *ptr;
-
-#ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
-		dev_kfree_skb(skb, FREE_WRITE);
-		return;
-	}
-#endif
-
-	skb->protocol = htons(ETH_P_AX25);
-	skb->dev      = ax25_fwd_dev(skb->dev);
-	skb->arp      = 1;
-
-	ptr    = skb_push(skb, 1);
-	*ptr++ = 0x00;			/* KISS */
-
-	dev_queue_xmit(skb);
-}
-
-/*
- *	IP over AX.25 encapsulation.
- */
-
-/*
- *	Shove an AX.25 UI header on an IP packet and handle ARP
- */
-
-#ifdef CONFIG_INET
-
-int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr,
-		void *saddr, unsigned len)
-{
-  	/* header is an AX.25 UI frame from us to them */
- 	unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
-
-  	*buff++ = 0;	/* KISS DATA */
-
-	if (daddr != NULL)
-		memcpy(buff, daddr, dev->addr_len);	/* Address specified */
-
-  	buff[6] &= ~AX25_CBIT;
-  	buff[6] &= ~AX25_EBIT;
-  	buff[6] |= AX25_SSSID_SPARE;
-  	buff += AX25_ADDR_LEN;
-
-  	if (saddr != NULL)
-  		memcpy(buff, saddr, dev->addr_len);
-  	else
-  		memcpy(buff, dev->dev_addr, dev->addr_len);
-
-  	buff[6] &= ~AX25_CBIT;
-  	buff[6] |= AX25_EBIT;
-  	buff[6] |= AX25_SSSID_SPARE;
-  	buff   += AX25_ADDR_LEN;
-
-  	*buff++ = AX25_UI;	/* UI */
-
-  	/* Append a suitable AX.25 PID */
-  	switch (type) {
-  		case ETH_P_IP:
-  			*buff++ = AX25_P_IP;
- 			break;
-  		case ETH_P_ARP:
-  			*buff++ = AX25_P_ARP;
-  			break;
-  		default:
-  			printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type);
-  			*buff++ = 0;
-  			break;
- 	}
-
-	if (daddr != NULL)
-	  	return AX25_HEADER_LEN;
-
-	return -AX25_HEADER_LEN;	/* Unfinished header */
+	printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.36 for Linux NET3.038 (Linux 2.1)\n");
 }
 
-int ax25_rebuild_header(struct sk_buff *skb)
-{
-	struct sk_buff *ourskb;
-	int mode;
-	unsigned char *bp  = skb->data;
-	struct device *dev = skb->dev;
-
-  	if (arp_find(bp + 1, skb))
-  		return 1;
-
-	if (bp[16] == AX25_P_IP) {
-		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
-		if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE))) {
-			/*
-			 *	We clone the buffer and release the original thereby
-			 *	keeping it straight
-			 *
-			 *	Note: we report 1 back so the caller will
-			 *	not feed the frame direct to the physical device
-			 *	We don't want that to happen. (It won't be upset
-			 *	as we have pulled the frame from the queue by
-			 *	freeing it).
-			 */
-			if ((ourskb = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-				dev_kfree_skb(skb, FREE_WRITE);
-				return 1;
-			}
-
-			if (skb->sk != NULL)
-				skb_set_owner_w(ourskb, skb->sk);
-
-			kfree_skb(skb, FREE_WRITE);
-
-			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
-
-			ax25_send_frame(ourskb, 1, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
-
-			return 1;
-		}
-	}
-
-  	bp[7]  &= ~AX25_CBIT;
-  	bp[7]  &= ~AX25_EBIT;
-  	bp[7]  |= AX25_SSSID_SPARE;
-
-  	bp[14] &= ~AX25_CBIT;
-  	bp[14] |= AX25_EBIT;
-  	bp[14] |= AX25_SSSID_SPARE;
-
-  	/*
-  	 * dl1bke 960317: we use ax25_queue_xmit here to allow mode datagram
-  	 *		  over ethernet. I don't know if this is valid, though.
-  	 */
-	ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
-
-	skb->dev      = dev;
-	skb->priority = SOPRI_NORMAL;
-
-	ax25_queue_xmit(skb);
-
-  	return 1;
-}
-
-#endif
-
 #ifdef MODULE
 int init_module(void)
 {
@@ -2609,6 +1817,8 @@
 	proc_net_unregister(PROC_NET_AX25_ROUTE);
 #endif
 	ax25_rt_free();
+	ax25_uid_free();
+	ax25_dev_free();
 
 	ax25_unregister_sysctl();
 
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_addr.c linux/net/ax25/ax25_addr.c
--- v2.1.26/linux/net/ax25/ax25_addr.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_addr.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,311 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	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.
+ *
+ *	Most of this code is based on the SDL diagrams published in the 7th
+ *	ARRL Computer Networking Conference papers. The diagrams have mistakes
+ *	in them, but are mostly correct. Before you modify the code could you
+ *	read the SDL diagrams as the code is not obvious and probably very
+ *	easy to break;
+ *
+ *	History
+ *	AX.25 036	Jonathan(G4KLX)	Split from ax25_subr.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ *	The null address is defined as a callsign of all spaces with an
+ *	SSID of zero.
+ */
+ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
+
+/*
+ *	ax25 -> ascii conversion
+ */
+char *ax2asc(ax25_address *a)
+{
+	static char buf[11];
+	char c, *s;
+	int n;
+
+	for (n = 0, s = buf; n < 6; n++) {
+		c = (a->ax25_call[n] >> 1) & 0x7F;
+
+		if (c != ' ') *s++ = c;
+	}
+
+	*s++ = '-';
+
+	if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
+		*s++ = '1';
+		n -= 10;
+	}
+
+	*s++ = n + '0';
+	*s++ = '\0';
+
+	if (*buf == '\0' || *buf == '-')
+	   return "*";
+
+	return buf;
+
+}
+
+/*
+ *	ascii -> ax25 conversion
+ */
+ax25_address *asc2ax(char *callsign)
+{
+	static ax25_address addr;
+	char *s;
+	int n;
+
+	for (s = callsign, n = 0; n < 6; n++) {
+		if (*s != '\0' && *s != '-')
+			addr.ax25_call[n] = *s++;
+		else
+			addr.ax25_call[n] = ' ';
+		addr.ax25_call[n] <<= 1;
+		addr.ax25_call[n] &= 0xFE;
+	}
+
+	if (*s++ == '\0') {
+		addr.ax25_call[6] = 0x00;
+		return &addr;
+	}
+
+	addr.ax25_call[6] = *s++ - '0';
+
+	if (*s != '\0') {
+		addr.ax25_call[6] *= 10;
+		addr.ax25_call[6] += *s++ - '0';
+	}
+
+	addr.ax25_call[6] <<= 1;
+	addr.ax25_call[6] &= 0x1E;
+
+	return &addr;
+}
+
+/*
+ *	Compare two ax.25 addresses
+ */
+int ax25cmp(ax25_address *a, ax25_address *b)
+{
+	int ct = 0;
+
+	while (ct < 6) {
+		if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE))	/* Clean off repeater bits */
+			return 1;
+		ct++;
+	}
+
+ 	if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E))	/* SSID without control bit */
+ 		return 0;
+
+ 	return 2;			/* Partial match */
+}
+
+/*
+ *	Compare two AX.25 digipeater paths.
+ */
+int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2)
+{
+	int i;
+
+	if (digi1->ndigi != digi2->ndigi)
+		return 1;
+
+	if (digi1->lastrepeat != digi2->lastrepeat)
+		return 1;
+
+	for (i = 0; i < digi1->ndigi; i++)
+		if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0)
+			return 1;
+
+	return 0;
+}
+
+/*
+ *	Given an AX.25 address pull of to, from, digi list, command/response and the start of data
+ *
+ */
+unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
+{
+	int d = 0;
+
+	if (len < 14) return NULL;
+
+	if (flags != NULL) {
+		*flags = 0;
+
+		if (buf[6] & AX25_CBIT)
+			*flags = AX25_COMMAND;
+		if (buf[13] & AX25_CBIT)
+			*flags = AX25_RESPONSE;
+	}
+
+	if (dama != NULL) 
+		*dama = ~buf[13] & AX25_DAMA_FLAG;
+
+	/* Copy to, from */
+	if (dest != NULL) 
+		memcpy(dest, buf + 0, AX25_ADDR_LEN);
+
+	if (src != NULL)  
+		memcpy(src,  buf + 7, AX25_ADDR_LEN);
+
+	buf += 2 * AX25_ADDR_LEN;
+	len -= 2 * AX25_ADDR_LEN;
+	digi->lastrepeat = -1;
+	digi->ndigi      = 0;
+
+	while (!(buf[-1] & AX25_EBIT)) {
+		if (d >= AX25_MAX_DIGIS)  return NULL;	/* Max of 6 digis */
+		if (len < 7) return NULL;	/* Short packet */
+
+		if (digi != NULL) {
+			memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
+			digi->ndigi = d + 1;
+			if (buf[6] & AX25_HBIT) {
+				digi->repeated[d] = 1;
+				digi->lastrepeat  = d;
+			} else {
+				digi->repeated[d] = 0;
+			}
+		}
+
+		buf += AX25_ADDR_LEN;
+		len -= AX25_ADDR_LEN;
+		d++;
+	}
+
+	return buf;
+}
+
+/*
+ *	Assemble an AX.25 header from the bits
+ */
+int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus)
+{
+	int len = 0;
+	int ct  = 0;
+
+	memcpy(buf, dest, AX25_ADDR_LEN);
+	buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+	buf[6] |= AX25_SSSID_SPARE;
+
+	if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
+
+	buf += AX25_ADDR_LEN;
+	len += AX25_ADDR_LEN;
+
+	memcpy(buf, src, AX25_ADDR_LEN);
+	buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+	buf[6] &= ~AX25_SSSID_SPARE;
+
+	if (modulus == AX25_MODULUS)
+		buf[6] |= AX25_SSSID_SPARE;
+	else
+		buf[6] |= AX25_ESSID_SPARE;
+
+	if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT;
+
+	/*
+	 *	Fast path the normal digiless path
+	 */
+	if (d == NULL || d->ndigi == 0) {
+		buf[6] |= AX25_EBIT;
+		return 2 * AX25_ADDR_LEN;
+	}	
+
+	buf += AX25_ADDR_LEN;
+	len += AX25_ADDR_LEN;
+
+	while (ct < d->ndigi) {
+		memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
+
+		if (d->repeated[ct])
+			buf[6] |= AX25_HBIT;
+		else
+			buf[6] &= ~AX25_HBIT;
+
+		buf[6] &= ~AX25_EBIT;
+		buf[6] |= AX25_SSSID_SPARE;
+
+		buf += AX25_ADDR_LEN;
+		len += AX25_ADDR_LEN;
+		ct++;
+	}
+
+	buf[-1] |= AX25_EBIT;
+
+	return len;
+}
+
+int ax25_addr_size(ax25_digi *dp)
+{
+	if (dp == NULL)
+		return 2 * AX25_ADDR_LEN;
+
+	return AX25_ADDR_LEN * (2 + dp->ndigi);
+}
+
+/* 
+ *	Reverse Digipeat List. May not pass both parameters as same struct
+ */
+void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
+{
+	int ct = 0;
+
+	out->ndigi      = in->ndigi;
+	out->lastrepeat = in->ndigi - in->lastrepeat - 2;
+
+	/* Invert the digipeaters */
+
+	while (ct < in->ndigi) {
+		out->calls[ct]    = in->calls[in->ndigi - ct - 1];
+		if (ct <= out->lastrepeat) {
+			out->calls[ct].ax25_call[6] |= AX25_HBIT;
+			out->repeated[ct]            = 1;
+		} else {
+			out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
+			out->repeated[ct]            = 0;
+		}
+		ct++;
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_dev.c linux/net/ax25/ax25_dev.c
--- v2.1.26/linux/net/ax25/ax25_dev.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_dev.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,215 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	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.
+ *
+ *	Other kernels modules in this kit are generally BSD derived. See the copyright headers.
+ *
+ *
+ *	History
+ *	AX.25 036	Jonathan(G4KLX)	Split from ax25_route.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+ax25_dev *ax25_dev_list = NULL;
+
+ax25_dev *ax25_dev_ax25dev(struct device *dev)
+{
+	ax25_dev *ax25_dev;
+
+	for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
+		if (ax25_dev->dev == dev)
+			return ax25_dev;
+
+	return NULL;
+}
+
+ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
+{
+	ax25_dev *ax25_dev;
+
+	for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
+		if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0)
+			return ax25_dev;
+
+	return NULL;
+}
+
+/*
+ *	This is called when an interface is brought up. These are
+ *	reasonable defaults.
+ */
+void ax25_dev_device_up(struct device *dev)
+{
+	ax25_dev *ax25_dev;
+	unsigned long flags;
+
+	if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
+		printk(KERN_ERR "ax25_dev_device_up out of memory\n");
+		return;
+	}
+
+	ax25_unregister_sysctl();
+
+	ax25_dev->dev     = dev;
+	ax25_dev->forward = NULL;
+
+	ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
+	ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
+	ax25_dev->values[AX25_VALUES_BACKOFF]   = AX25_DEF_BACKOFF;
+	ax25_dev->values[AX25_VALUES_CONMODE]   = AX25_DEF_CONMODE;
+	ax25_dev->values[AX25_VALUES_WINDOW]    = AX25_DEF_WINDOW;
+	ax25_dev->values[AX25_VALUES_EWINDOW]   = AX25_DEF_EWINDOW;
+	ax25_dev->values[AX25_VALUES_T1]        = AX25_DEF_T1;
+	ax25_dev->values[AX25_VALUES_T2]        = AX25_DEF_T2;
+	ax25_dev->values[AX25_VALUES_T3]        = AX25_DEF_T3;
+	ax25_dev->values[AX25_VALUES_IDLE]	= AX25_DEF_IDLE;
+	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
+	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
+	ax25_dev->values[AX25_VALUES_PROTOCOL]  = AX25_DEF_PROTOCOL;
+
+	save_flags(flags); cli();
+	ax25_dev->next = ax25_dev_list;
+	ax25_dev_list  = ax25_dev;
+	restore_flags(flags);
+
+	ax25_register_sysctl();
+}
+
+void ax25_dev_device_down(struct device *dev)
+{
+	ax25_dev *s, *ax25_dev;
+	unsigned long flags;
+
+	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+		return;
+
+	ax25_unregister_sysctl();
+
+	save_flags(flags); cli();
+
+	/*
+	 *	Remove any packet forwarding that points to this device.
+	 */
+	for (s = ax25_dev_list; s != NULL; s = s->next)
+		if (s->forward == dev)
+			s->forward = NULL;
+
+	if ((s = ax25_dev_list) == ax25_dev) {
+		ax25_dev_list = s->next;
+		restore_flags(flags);
+		kfree_s(ax25_dev, sizeof(ax25_dev));
+		ax25_register_sysctl();
+		return;
+	}
+
+	while (s != NULL && s->next != NULL) {
+		if (s->next == ax25_dev) {
+			s->next = ax25_dev->next;
+			restore_flags(flags);
+			kfree_s(ax25_dev, sizeof(ax25_dev));
+			ax25_register_sysctl();
+			return;
+		}
+
+		s = s->next;
+	}
+
+	restore_flags(flags);
+	ax25_register_sysctl();
+}
+
+int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
+{
+	ax25_dev *ax25_dev, *fwd_dev;
+
+	if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL)
+		return -EINVAL;
+
+	switch (cmd) {
+		case SIOCAX25ADDFWD:
+			if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL)
+				return -EINVAL;
+			if (ax25_dev->forward != NULL)
+				return -EINVAL;
+			ax25_dev->forward = fwd_dev->dev;
+			break;
+
+		case SIOCAX25DELFWD:
+			if (ax25_dev->forward == NULL)
+				return -EINVAL;
+			ax25_dev->forward = NULL;
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct device *ax25_fwd_dev(struct device *dev)
+{
+	ax25_dev *ax25_dev;
+
+	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+		return dev;
+
+	if (ax25_dev->forward == NULL)
+		return dev;
+
+	return ax25_dev->forward;
+}
+
+#ifdef MODULE
+
+/*
+ *	Free all memory associated with device structures.
+ */
+void ax25_dev_free(void)
+{
+	ax25_dev *s, *ax25_dev = ax25_dev_list;
+
+	while (ax25_dev != NULL) {
+		s        = ax25_dev;
+		ax25_dev = ax25_dev->next;
+
+		kfree_s(s, sizeof(ax25_dev));
+	}
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_iface.c linux/net/ax25/ax25_iface.c
--- v2.1.26/linux/net/ax25/ax25_iface.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_iface.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,282 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	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
+ *	AX.25 036	Jonathan(G4KLX)	Split from ax25_timer.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+static struct protocol_struct {
+	struct protocol_struct *next;
+	unsigned int pid;
+	int (*func)(struct sk_buff *, ax25_cb *);
+} *protocol_list = NULL;
+
+static struct linkfail_struct {
+	struct linkfail_struct *next;
+	void (*func)(ax25_address *, struct device *);
+} *linkfail_list = NULL;
+
+static struct listen_struct {
+	struct listen_struct *next;
+	ax25_address  callsign;
+	struct device *dev;
+} *listen_list = NULL;
+
+int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
+{
+	struct protocol_struct *protocol;
+	unsigned long flags;
+
+	if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
+		return 0;
+#ifdef CONFIG_INET
+	if (pid == AX25_P_IP || pid == AX25_P_ARP)
+		return 0;
+#endif
+	if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	protocol->pid  = pid;
+	protocol->func = func;
+
+	save_flags(flags);
+	cli();
+
+	protocol->next = protocol_list;
+	protocol_list  = protocol;
+
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_protocol_release(unsigned int pid)
+{
+	struct protocol_struct *s, *protocol = protocol_list;
+	unsigned long flags;
+
+	if (protocol == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (protocol->pid == pid) {
+		protocol_list = protocol->next;
+		restore_flags(flags);
+		kfree_s(protocol, sizeof(struct protocol_struct));
+		return;
+	}
+
+	while (protocol != NULL && protocol->next != NULL) {
+		if (protocol->next->pid == pid) {
+			s = protocol->next;
+			protocol->next = protocol->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct protocol_struct));
+			return;
+		}
+
+		protocol = protocol->next;
+	}
+
+	restore_flags(flags);
+}
+
+int ax25_linkfail_register(void (*func)(ax25_address *, struct device *))
+{
+	struct linkfail_struct *linkfail;
+	unsigned long flags;
+
+	if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	linkfail->func = func;
+
+	save_flags(flags);
+	cli();
+
+	linkfail->next = linkfail_list;
+	linkfail_list  = linkfail;
+
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_linkfail_release(void (*func)(ax25_address *, struct device *))
+{
+	struct linkfail_struct *s, *linkfail = linkfail_list;
+	unsigned long flags;
+
+	if (linkfail == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (linkfail->func == func) {
+		linkfail_list = linkfail->next;
+		restore_flags(flags);
+		kfree_s(linkfail, sizeof(struct linkfail_struct));
+		return;
+	}
+
+	while (linkfail != NULL && linkfail->next != NULL) {
+		if (linkfail->next->func == func) {
+			s = linkfail->next;
+			linkfail->next = linkfail->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct linkfail_struct));
+			return;
+		}
+
+		linkfail = linkfail->next;
+	}
+
+	restore_flags(flags);
+}
+
+int ax25_listen_register(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *listen;
+	unsigned long flags;
+
+	if (ax25_listen_mine(callsign, dev))
+		return 0;
+
+	if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
+		return 0;
+
+	listen->callsign = *callsign;
+	listen->dev      = dev;
+
+	save_flags(flags);
+	cli();
+
+	listen->next = listen_list;
+	listen_list  = listen;
+
+	restore_flags(flags);
+
+	return 1;
+}
+
+void ax25_listen_release(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *s, *listen = listen_list;
+	unsigned long flags;
+
+	if (listen == NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
+		listen_list = listen->next;
+		restore_flags(flags);
+		kfree_s(listen, sizeof(struct listen_struct));
+		return;
+	}
+
+	while (listen != NULL && listen->next != NULL) {
+		if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
+			s = listen->next;
+			listen->next = listen->next->next;
+			restore_flags(flags);
+			kfree_s(s, sizeof(struct listen_struct));
+			return;
+		}
+
+		listen = listen->next;
+	}
+
+	restore_flags(flags);
+}
+
+int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
+{
+	struct protocol_struct *protocol;
+
+	for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+		if (protocol->pid == pid)
+			return protocol->func;
+
+	return NULL;
+}
+
+int ax25_listen_mine(ax25_address *callsign, struct device *dev)
+{
+	struct listen_struct *listen;
+
+	for (listen = listen_list; listen != NULL; listen = listen->next)
+		if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
+			return 1;
+
+	return 0;
+}
+
+void ax25_link_failed(ax25_address *callsign, struct device *dev)
+{
+	struct linkfail_struct *linkfail;
+
+	for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
+		(linkfail->func)(callsign, dev);
+}
+
+/*
+ *	Return the state of an AX.25 link given source, destination, and
+ *	device.
+ */
+int ax25_link_up(ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev)
+{
+	return ax25_find_cb(src, dest, digi, dev) != NULL;
+}
+
+int ax25_protocol_is_registered(unsigned int pid)
+{
+	struct protocol_struct *protocol;
+
+	for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+		if (protocol->pid == pid)
+			return 1;
+
+	return 0;
+}
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v2.1.26/linux/net/ax25/ax25_in.c	Sun Feb  2 05:18:48 1997
+++ linux/net/ax25/ax25_in.c	Tue Feb 25 17:12:51 1997
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 035
+ *	AX.25 release 036
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -36,6 +36,7 @@
  *	AX.25 033	Jonathan(G4KLX)	Remove auto-router.
  *					Modularisation changes.
  *	AX.25 035	Hans(PE1AYX)	Fixed interface to IP layer.
+ *	AX.25 036	Jonathan(G4KLX)	Move DAMA code into own file.
  */
 
 #include <linux/config.h>
@@ -54,16 +55,16 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/firewall.h>
 #include <net/sock.h>
 #include <net/ip.h>			/* For ip_rcv */
+#include <net/arp.h>			/* For arp_rcv */
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 
-static int ax25_rx_iframe(ax25_cb *, struct sk_buff *);
-
 /*
  *	Given a fragment, queue it on the fragment queue and if the fragment
  *	is complete, send it back to ax25_rx_iframe.
@@ -90,7 +91,7 @@
 						return 1;
 					}
 
-					skbn->dev = ax25->device;
+					skbn->dev = ax25->ax25_dev->dev;
 
 					skb_reserve(skbn, AX25_MAX_HEADER_LEN);
 
@@ -143,7 +144,7 @@
  *	This is where all valid I frames are sent to, to be dispatched to
  *	whichever protocol requires them.
  */
-static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
 {
 	int (*func)(struct sk_buff *, ax25_cb *);
 	volatile int queued = 0;
@@ -160,9 +161,9 @@
 		skb_pull(skb, 1);	/* Remove PID */
 		skb->h.raw    = skb->data;
 		skb->nh.raw   = skb->data;
-		skb->dev      = ax25->device;
+		skb->dev      = ax25->ax25_dev->dev;
 		skb->pkt_type = PACKET_HOST;
-		ip_rcv(skb, ax25->device, NULL);	/* Wrong ptype */
+		ip_rcv(skb, skb->dev, NULL);	/* Wrong ptype */
 		return 1;
 	}
 #endif
@@ -176,7 +177,7 @@
 		return (*func)(skb, ax25);
 	}
 
-	if (ax25->sk != NULL && ax25_dev_get_value(ax25->device, AX25_VALUES_TEXT) && ax25->sk->protocol == pid) {
+	if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2 && ax25->sk->protocol == pid) {
 		if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
 			queued = 1;
 		else
@@ -187,552 +188,304 @@
 }
 
 /*
- *	State machine for state 1, Awaiting Connection State.
- *	The handling of the timer(s) is in file ax25_timer.c.
- *	Handling of state 0 and connection release is in ax25.c.
+ *	Higher level upcall for a LAPB frame
  */
-static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type, int dama)
+static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type)
 {
-	switch (frametype) {
-		case AX25_SABM:
-			ax25->modulus = AX25_MODULUS;
-			ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			break;
+	int queued = 0;
 
-		case AX25_SABME:
-			ax25->modulus = AX25_EMODULUS;
-			ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			break;
+	if (ax25->state == AX25_STATE_0)
+		return 0;
 
-		case AX25_DISC:
-			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
-			break;
+	del_timer(&ax25->timer);
 
-		case AX25_UA:
-			if (pf || dama) {
-				if (dama) ax25_dama_on(ax25); /* bke */
-				ax25_calculate_rtt(ax25);
-				ax25->t1timer = 0;
-				ax25->t3timer = ax25->t3;
-				ax25->idletimer = ax25->idle;
-				ax25->vs      = 0;
-				ax25->va      = 0;
-				ax25->vr      = 0;
-				ax25->state   = AX25_STATE_3;
-				ax25->n2count = 0;
-				ax25->dama_slave = dama;	/* bke */
-				if (ax25->sk != NULL) {
-					ax25->sk->state = TCP_ESTABLISHED;
-					/* For WAIT_SABM connections we will produce an accept ready socket here */
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-				}
-			}
+	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+		case AX25_PROTO_STD:
+			queued = ax25_std_frame_in(ax25, skb, type);
+			break;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+		case AX25_PROTO_DAMA_SLAVE:
+			queued = ax25_ds_frame_in(ax25, skb, type);
 			break;
+#endif
+	}
+
+	ax25_set_timer(ax25);
+
+	return queued;
+}
 
-		case AX25_DM:
-			if (pf) {
-				if (ax25->modulus == AX25_MODULUS) {
-					ax25_clear_queues(ax25);
-					ax25->state = AX25_STATE_0;
-					if (ax25->sk != NULL) {
-						ax25->sk->state     = TCP_CLOSE;
-						ax25->sk->err       = ECONNREFUSED;
-						ax25->sk->shutdown |= SEND_SHUTDOWN;
-						if (!ax25->sk->dead)
-							ax25->sk->state_change(ax25->sk);
-						ax25->sk->dead      = 1;
+static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype)
+{
+	struct sock *make;
+	struct sock *sk;
+	int type = 0;
+	ax25_digi dp, reverse_dp;
+	ax25_cb *ax25;
+	ax25_address src, dest;
+	ax25_address *next_digi = NULL;
+	ax25_dev *ax25_dev;
+	struct sock *raw;
+	int mine = 0;
+	int dama;
+
+	/*
+	 *	Process the AX.25/LAPB frame.
+	 */
+
+	skb->h.raw = skb->data;
+
+	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+#ifdef CONFIG_FIREWALL
+	if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL) != FW_ACCEPT) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+#endif
+
+	/*
+	 *	Parse the address header.
+	 */
+
+	if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	/*
+	 *	Ours perhaps ?
+	 */
+	if (dp.lastrepeat + 1 < dp.ndigi)		/* Not yet digipeated completely */
+		next_digi = &dp.calls[dp.lastrepeat + 1];
+
+	/*
+	 *	Pull of the AX.25 headers leaving the CTRL/PID bytes
+	 */
+	skb_pull(skb, ax25_addr_size(&dp));
+
+	/* For our port addresses ? */
+	if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
+		mine = 1;
+
+	/* Also match on any registered callsign from L3/4 */
+	if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
+		mine = 1;
+
+	/* UI frame - bypass LAPB processing */
+	if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
+		skb->h.raw = skb->data + 2;		/* skip control and pid */
+
+		if ((raw = ax25_addr_match(&dest)) != NULL)
+			ax25_send_to_raw(raw, skb, skb->data[1]);
+
+		if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+
+		/* Now we are pointing at the pid byte */
+		switch (skb->data[1]) {
+#ifdef CONFIG_INET
+			case AX25_P_IP:
+				skb_pull(skb,2);		/* drop PID/CTRL */
+				skb->h.raw    = skb->data;
+				skb->nh.raw   = skb->data;
+				skb->dev      = dev;
+				skb->pkt_type = PACKET_HOST;
+				ip_rcv(skb, dev, ptype);	/* Note ptype here is the wrong one, fix me later */
+				break;
+
+			case AX25_P_ARP:
+				skb_pull(skb,2);
+				skb->h.raw    = skb->data;
+				skb->nh.raw   = skb->data;
+				skb->dev      = dev;
+				skb->pkt_type = PACKET_HOST;
+				arp_rcv(skb, dev, ptype);	/* Note ptype here is wrong... */
+				break;
+#endif
+			case AX25_P_TEXT:
+				/* Now find a suitable dgram socket */
+				if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
+					if (sk->rmem_alloc >= sk->rcvbuf) {
+						kfree_skb(skb, FREE_READ);
+					} else {
+						/*
+						 *	Remove the control and PID.
+						 */
+						skb_pull(skb, 2);
+						if (sock_queue_rcv_skb(sk, skb) != 0)
+							kfree_skb(skb, FREE_READ);
 					}
 				} else {
-					ax25->modulus = AX25_MODULUS;
-					ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+					kfree_skb(skb, FREE_READ);
 				}
-			}
-			break;
+				break;
 
-		default:
-			if (dama && pf)
-				ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
-			break;
+			default:
+				kfree_skb(skb, FREE_READ);	/* Will scan SOCK_AX25 RAW sockets */
+				break;
+		}
+
+		return 0;
 	}
 
-	return 0;
-}
+	/*
+	 *	Is connected mode supported on this device ?
+	 *	If not, should we DM the incoming frame (except DMs) or
+	 *	silently ignore them. For now we stay quiet.
+	 */
+	if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
 
-/*
- *	State machine for state 2, Awaiting Release State.
- *	The handling of the timer(s) is in file ax25_timer.c
- *	Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
-	switch (frametype) {
-		case AX25_SABM:
-		case AX25_SABME:
-			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
-			if (ax25->dama_slave)
-				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-			break;
+	/* LAPB */
 
-		case AX25_DISC:
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			if (ax25->dama_slave) {
-				ax25->state = AX25_STATE_0;
-				ax25_dama_off(ax25);
-				if (ax25->sk != NULL) {
-					ax25->sk->state     = TCP_CLOSE;
-					ax25->sk->err       = 0;
-					ax25->sk->shutdown |= SEND_SHUTDOWN;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead      = 1;
-				}
-			}
-			break;
+	/* AX.25 state 1-4 */
 
-		case AX25_DM:
-		case AX25_UA:
-			if (pf) {
-				ax25->state = AX25_STATE_0;
-				ax25_dama_off(ax25);
-				if (ax25->sk != NULL) {
-					ax25->sk->state     = TCP_CLOSE;
-					ax25->sk->err       = 0;
-					ax25->sk->shutdown |= SEND_SHUTDOWN;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead      = 1;
-				}
-			}
-			break;
+	ax25_digi_invert(&dp, &reverse_dp);
 
-		case AX25_I:
-		case AX25_REJ:
-		case AX25_RNR:
-		case AX25_RR:
-			if (pf) {
-				if (ax25->dama_slave)
-					ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-				else
-					ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
-			}
-			break;
+	if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
+		/*
+		 *	Process the frame. If it is queued up internally it returns one otherwise we
+		 *	free it immediately. This routine itself wakes the user context layers so we
+		 *	do no further work
+		 */
+		if (ax25_process_rx_frame(ax25, skb, type) == 0)
+			kfree_skb(skb, FREE_READ);
 
-		default:
-			break;
+		return 0;
 	}
 
-	return 0;
-}
+	/* AX.25 state 0 (disconnected) */
 
-/*
- *	State machine for state 3, Connected State.
- *	The handling of the timer(s) is in file ax25_timer.c
- *	Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama)
-{
-	int queued = 0;
+	/* a) received not a SABM(E) */
 
-	switch (frametype) {
-		case AX25_SABM:
-		case AX25_SABME:
-			if (dama) ax25_dama_on(ax25);
-			if (frametype == AX25_SABM) {
-				ax25->modulus = AX25_MODULUS;
-				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
-			} else {
-				ax25->modulus = AX25_EMODULUS;
-				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
-			}
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			ax25->condition = 0x00;
-			ax25->t1timer   = 0;
-			ax25->t3timer   = ax25->t3;
-			ax25->idletimer = ax25->idle;
-			ax25->vs        = 0;
-			ax25->va        = 0;
-			ax25->vr        = 0;
-			ax25->dama_slave = dama;
-			ax25_requeue_frames(ax25);
-			break;
+	if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
+		/*
+		 *	Never reply to a DM. Also ignore any connects for
+		 *	addresses that are not our interfaces and not a socket.
+		 */
+		if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
+			ax25_return_dm(dev, &src, &dest, &dp);
 
-		case AX25_DISC:
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			if (ax25->sk != NULL) {
-				ax25->sk->state     = TCP_CLOSE;
-				ax25->sk->err       = 0;
-				ax25->sk->shutdown |= SEND_SHUTDOWN;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead      = 1;
-			}
-			break;
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
 
-		case AX25_DM:
-			ax25_clear_queues(ax25);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			if (ax25->sk != NULL) {
-				ax25->sk->state     = TCP_CLOSE;
-				ax25->sk->err       = ECONNRESET;
-				ax25->sk->shutdown |= SEND_SHUTDOWN;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead      = 1;
-			}
-			break;
+	/* b) received SABM(E) */
 
-		case AX25_RR:
-		case AX25_RNR:
-			if (frametype == AX25_RR)
-				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-			else
-				ax25->condition |= AX25_COND_PEER_RX_BUSY;
-			ax25_check_need_response(ax25, type, pf);
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_check_iframes_acked(ax25, nr);
-				dama_check_need_response(ax25, type, pf);
-			} else {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
+	if (dp.lastrepeat + 1 == dp.ndigi)
+		sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
+	else
+		sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
+
+	if (sk != NULL) {
+		if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
+			if (mine) ax25_return_dm(dev, &src, &dest, &dp);
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
 
-		case AX25_REJ:
-			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-			ax25_check_need_response(ax25, type, pf);
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_frames_acked(ax25, nr);
-				ax25_calculate_rtt(ax25);
-				ax25->t1timer = 0;
-				ax25->t3timer = ax25->t3;
-				ax25_requeue_frames(ax25);
-				dama_check_need_response(ax25, type, pf);
-			} else {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
+		ax25 = make->protinfo.ax25;
 
-		case AX25_I:
-			if (!ax25_validate_nr(ax25, nr)) {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-				break;
-			}
-			if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
-				ax25_frames_acked(ax25, nr);
-			} else {
-				ax25_check_iframes_acked(ax25, nr);
-			}
-			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
-				if (pf)	{
-					if (ax25->dama_slave)
-						dama_enquiry_response(ax25);
-					else
-						ax25_enquiry_response(ax25);
-				}
-				break;
-			}
-			if (ns == ax25->vr) {
-				ax25->vr = (ax25->vr + 1) % ax25->modulus;
-				queued = ax25_rx_iframe(ax25, skb);
-				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
-					ax25->vr = ns;	/* ax25->vr - 1 */
-					if (pf) {
-						if (ax25->dama_slave)
-							dama_enquiry_response(ax25);
-						else
-							ax25_enquiry_response(ax25);
-					}
-					break;
-				}
-				ax25->condition &= ~AX25_COND_REJECT;
-				if (pf) {
-					if (ax25->dama_slave)
-						dama_enquiry_response(ax25);
-					else
-						ax25_enquiry_response(ax25);
-				} else {
-					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
-						ax25->t2timer = ax25->t2;
-						ax25->condition |= AX25_COND_ACK_PENDING;
-					}
-				}
-			} else {
-				if (ax25->condition & AX25_COND_REJECT) {
-					if (pf) {
-						if (ax25->dama_slave)
-							dama_enquiry_response(ax25);
-						else
-							ax25_enquiry_response(ax25);
-					}
-				} else {
-					ax25->condition |= AX25_COND_REJECT;
-					if (ax25->dama_slave)
-						dama_enquiry_response(ax25);
-					else
-						ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
-					ax25->condition &= ~AX25_COND_ACK_PENDING;
-				}
-			}
-			break;
+		skb_queue_head(&sk->receive_queue, skb);
 
-		case AX25_FRMR:
-		case AX25_ILLEGAL:
-			ax25_establish_data_link(ax25);
-			ax25->state = AX25_STATE_1;
-			break;
+		skb->sk     = make;
+		make->state = TCP_ESTABLISHED;
+		make->pair  = sk;
 
-		default:
-			break;
+		sk->ack_backlog++;
+	} else {
+		if (!mine) {
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+
+		if ((ax25 = ax25_create_cb()) == NULL) {
+			ax25_return_dm(dev, &src, &dest, &dp);
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+
+		ax25_fillin_cb(ax25, ax25_dev);
+		ax25->idletimer = ax25->idle;
 	}
 
-	return queued;
-}
+	ax25->source_addr = dest;
+	ax25->dest_addr   = src;
 
-/*
- *	State machine for state 4, Timer Recovery State.
- *	The handling of the timer(s) is in file ax25_timer.c
- *	Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama)
-{
-	int queued = 0;
+	/*
+	 *	Sort out any digipeated paths.
+	 */
+	if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+		kfree_skb(skb, FREE_READ);
+		ax25_destroy_socket(ax25);
+		return 0;
+	}
 
-	switch (frametype) {
-		case AX25_SABM:
-		case AX25_SABME:
-			if (dama) ax25_dama_on(ax25);
-			if (frametype == AX25_SABM) {
-				ax25->modulus = AX25_MODULUS;
-				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
-			} else {
-				ax25->modulus = AX25_EMODULUS;
-				ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
-			}
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			ax25->condition = 0x00;
-			ax25->t1timer   = 0;
-			ax25->t3timer   = ax25->t3;
-			ax25->idletimer = ax25->idle;
-			ax25->vs        = 0;
-			ax25->va        = 0;
-			ax25->vr        = 0;
-			ax25->state     = AX25_STATE_3;
-			ax25->n2count   = 0;
-			ax25->dama_slave = dama;
-			ax25_requeue_frames(ax25);
-			break;
+	if (dp.ndigi == 0) {
+		if (ax25->digipeat != NULL) {
+			kfree_s(ax25->digipeat, sizeof(ax25_digi));
+			ax25->digipeat = NULL;
+		}
+	} else {
+		/* Reverse the source SABM's path */
+		*ax25->digipeat = reverse_dp;
+	}
 
-		case AX25_DISC:
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			if (ax25->sk != NULL) {
-				ax25->sk->state     = TCP_CLOSE;
-				ax25->sk->err       = 0;
-				ax25->sk->shutdown |= SEND_SHUTDOWN;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead      = 1;
-			}
-			break;
+	if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+		ax25->modulus = AX25_EMODULUS;
+		ax25->window  = ax25_dev->values[AX25_VALUES_EWINDOW];
+	} else {
+		ax25->modulus = AX25_MODULUS;
+		ax25->window  = ax25_dev->values[AX25_VALUES_WINDOW];
+	}
 
-		case AX25_DM:
-			ax25_clear_queues(ax25);
-			ax25->t3timer = 0;
-			ax25->state   = AX25_STATE_0;
-			ax25_dama_off(ax25);
-			if (ax25->sk != NULL) {
-				ax25->sk->state     = TCP_CLOSE;
-				ax25->sk->err       = ECONNRESET;
-				ax25->sk->shutdown |= SEND_SHUTDOWN;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead      = 1;
-			}
-			break;
+	ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
 
-		case AX25_RR:
-		case AX25_RNR:
-			if (frametype == AX25_RR)
-				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-			else
-				ax25->condition |= AX25_COND_PEER_RX_BUSY;
-			if (type == AX25_RESPONSE && pf) {
-				ax25->t1timer = 0;
-				if (ax25_validate_nr(ax25, nr)) {
-					ax25_frames_acked(ax25, nr);
-					if (ax25->vs == ax25->va) {
-						ax25->t3timer = ax25->t3;
-						ax25->n2count = 0;
-						ax25->state   = AX25_STATE_3;
-					}
-				} else {
-					ax25_nr_error_recovery(ax25);
-					ax25->state = AX25_STATE_1;
-				}
-				break;
-			}
-			ax25_check_need_response(ax25, type, pf);
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_frames_acked(ax25, nr);
-				dama_check_need_response(ax25, type, pf);
-			} else {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+	if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
+		ax25_dama_on(ax25);
+#endif
 
-		case AX25_REJ:
-			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-			if (pf && (type == AX25_RESPONSE || (ax25->dama_slave && type == AX25_COMMAND))) {
-				ax25->t1timer = 0;
-				if (ax25_validate_nr(ax25, nr)) {
-					ax25_frames_acked(ax25, nr);
-					if (ax25->vs == ax25->va) {
-						ax25->t3timer = ax25->t3;
-						ax25->n2count = 0;
-						ax25->state   = AX25_STATE_3;
-					} else {
-						ax25_requeue_frames(ax25);
-					}
-					dama_check_need_response(ax25, type, pf);
-				} else {
-					ax25_nr_error_recovery(ax25);
-					ax25->state = AX25_STATE_1;
-				}
-				break;
-			}
-			ax25_check_need_response(ax25, type, pf);
-			if (ax25_validate_nr(ax25, nr)) {
-				ax25_frames_acked(ax25, nr);
-				if(ax25->vs != ax25->va) {
-					ax25_requeue_frames(ax25);
-				}
-				dama_check_need_response(ax25, type, pf);
-			} else {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-			}
-			break;
+	ax25->t3timer = ax25->t3;
+	ax25->state   = AX25_STATE_3;
 
-		case AX25_I:
-			if (!ax25_validate_nr(ax25, nr)) {
-				ax25_nr_error_recovery(ax25);
-				ax25->state = AX25_STATE_1;
-				break;
-			}
-			ax25_frames_acked(ax25, nr);
-			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
-				if (pf) {
-					if (ax25->dama_slave)
-						ax25_enquiry_response(ax25);
-					else
-						dama_enquiry_response(ax25);
-				}
-				break;
-			}
-			if (ns == ax25->vr) {
-				ax25->vr = (ax25->vr + 1) % ax25->modulus;
-				queued = ax25_rx_iframe(ax25, skb);
-				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
-					ax25->vr = ns;	/* ax25->vr - 1 */
-					if (pf) {
-						if (ax25->dama_slave)
-							dama_enquiry_response(ax25);
-						else
-							ax25_enquiry_response(ax25);
-					}
-					break;
-				}
-				ax25->condition &= ~AX25_COND_REJECT;
-				if (pf) {
-					if (ax25->dama_slave)
-						dama_enquiry_response(ax25);
-					else
-						ax25_enquiry_response(ax25);
-				} else {
-					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
-						ax25->t2timer = ax25->t2;
-						ax25->condition |= AX25_COND_ACK_PENDING;
-					}
-				}
-			} else {
-				if (ax25->condition & AX25_COND_REJECT) {
-					if (pf) {
-						if (ax25->dama_slave)
-							dama_enquiry_response(ax25);
-						else
-							ax25_enquiry_response(ax25);
-					}
-				} else {
-					ax25->condition |= AX25_COND_REJECT;
-					if (ax25->dama_slave)
-						dama_enquiry_response(ax25);
-					else
-						ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
-					ax25->condition &= ~AX25_COND_ACK_PENDING;
-				}
-			}
-			break;
+	ax25_insert_socket(ax25);
 
-		case AX25_FRMR:
-		case AX25_ILLEGAL:
-			ax25_establish_data_link(ax25);
-			ax25->state = AX25_STATE_1;
-			break;
+	ax25_set_timer(ax25);
 
-		default:
-			break;
+	if (sk != NULL) {
+		if (!sk->dead)
+			sk->data_ready(sk, skb->len);
+	} else {
+		kfree_skb(skb, FREE_READ);
 	}
 
-	return queued;
+	return 0;
 }
 
 /*
- *	Higher level upcall for a LAPB frame
+ *	Receive an AX.25 frame via a SLIP interface.
  */
-int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
+int ax25_kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
 {
-	int queued = 0, frametype, ns, nr, pf;
+	skb->sk = NULL;		/* Initially we don't know who it's for */
 
-	if (ax25->state == AX25_STATE_0)
+	if ((*skb->data & 0x0F) != 0) {
+		kfree_skb(skb, FREE_READ);	/* Not a KISS data frame */
 		return 0;
-
-	del_timer(&ax25->timer);
-
-	frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
-
-	switch (ax25->state) {
-		case AX25_STATE_1:
-			queued = ax25_state1_machine(ax25, skb, frametype, pf, type, dama);
-			break;
-		case AX25_STATE_2:
-			queued = ax25_state2_machine(ax25, skb, frametype, pf, type);
-			break;
-		case AX25_STATE_3:
-			queued = ax25_state3_machine(ax25, skb, frametype, ns, nr, pf, type, dama);
-			break;
-		case AX25_STATE_4:
-			queued = ax25_state4_machine(ax25, skb, frametype, ns, nr, pf, type, dama);
-			break;
 	}
 
-	ax25_set_timer(ax25);
+	skb_pull(skb, AX25_KISS_HEADER_LEN);	/* Remove the KISS byte */
 
-	return queued;
+	return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
 }
 
 #endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_ip.c linux/net/ax25/ax25_ip.c
--- v2.1.26/linux/net/ax25/ax25_ip.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_ip.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,173 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ *	releases, misbehave and/or generally screw up. It might even work.
+ *
+ *	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
+ *	AX.25 036	Jonathan(G4KLX)	Split from af_ax25.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/sysctl.h>
+#include <net/ip.h>
+#include <net/arp.h>
+
+/*
+ *	IP over AX.25 encapsulation.
+ */
+
+/*
+ *	Shove an AX.25 UI header on an IP packet and handle ARP
+ */
+
+#ifdef CONFIG_INET
+
+int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+  	/* header is an AX.25 UI frame from us to them */
+ 	unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
+
+  	*buff++ = 0x00;	/* KISS DATA */
+
+	if (daddr != NULL)
+		memcpy(buff, daddr, dev->addr_len);	/* Address specified */
+
+  	buff[6] &= ~AX25_CBIT;
+  	buff[6] &= ~AX25_EBIT;
+  	buff[6] |= AX25_SSSID_SPARE;
+  	buff    += AX25_ADDR_LEN;
+
+  	if (saddr != NULL)
+  		memcpy(buff, saddr, dev->addr_len);
+  	else
+  		memcpy(buff, dev->dev_addr, dev->addr_len);
+
+  	buff[6] &= ~AX25_CBIT;
+  	buff[6] |= AX25_EBIT;
+  	buff[6] |= AX25_SSSID_SPARE;
+  	buff    += AX25_ADDR_LEN;
+
+  	*buff++  = AX25_UI;	/* UI */
+
+  	/* Append a suitable AX.25 PID */
+  	switch (type) {
+  		case ETH_P_IP:
+  			*buff++ = AX25_P_IP;
+ 			break;
+  		case ETH_P_ARP:
+  			*buff++ = AX25_P_ARP;
+  			break;
+  		default:
+  			printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type);
+  			*buff++ = 0;
+  			break;
+ 	}
+
+	if (daddr != NULL)
+	  	return AX25_HEADER_LEN;
+
+	return -AX25_HEADER_LEN;	/* Unfinished header */
+}
+
+int ax25_rebuild_header(struct sk_buff *skb)
+{
+	struct sk_buff *ourskb;
+	int mode;
+	unsigned char *bp  = skb->data;
+	struct device *dev = skb->dev;
+	ax25_dev *ax25_dev;
+
+  	if (arp_find(bp + 1, skb))
+  		return 1;
+
+	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+		return 1;
+
+	if (bp[16] == AX25_P_IP) {
+		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
+		if (mode == 'V' || (mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
+			/*
+			 *	We clone the buffer and release the original thereby
+			 *	keeping it straight
+			 *
+			 *	Note: we report 1 back so the caller will
+			 *	not feed the frame direct to the physical device
+			 *	We don't want that to happen. (It won't be upset
+			 *	as we have pulled the frame from the queue by
+			 *	freeing it).
+			 */
+			if ((ourskb = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+				dev_kfree_skb(skb, FREE_WRITE);
+				return 1;
+			}
+
+			if (skb->sk != NULL)
+				skb_set_owner_w(ourskb, skb->sk);
+
+			kfree_skb(skb, FREE_WRITE);
+
+			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
+
+			ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
+
+			return 1;
+		}
+	}
+
+  	bp[7]  &= ~AX25_CBIT;
+  	bp[7]  &= ~AX25_EBIT;
+  	bp[7]  |= AX25_SSSID_SPARE;
+
+  	bp[14] &= ~AX25_CBIT;
+  	bp[14] |= AX25_EBIT;
+  	bp[14] |= AX25_SSSID_SPARE;
+
+	ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
+
+	skb->dev      = dev;
+	skb->priority = SOPRI_NORMAL;
+
+	ax25_queue_xmit(skb);
+
+  	return 1;
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.1.26/linux/net/ax25/ax25_out.c	Sun Feb  2 05:18:48 1997
+++ linux/net/ax25/ax25_out.c	Tue Feb 25 17:12:51 1997
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 035
+ *	AX.25 release 036
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -47,6 +47,7 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/firewall.h>
 #include <net/sock.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -54,32 +55,94 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 
+int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev)
+{
+	ax25_dev *ax25_dev;
+	ax25_cb *ax25;
+
+	if (skb == NULL)
+		return 0;
+
+	/*
+	 * Look for an existing connection.
+	 */
+	if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
+		ax25_output(ax25, paclen, skb);
+		ax25->idletimer = ax25->idle;
+		return 1;		/* It already existed */
+	}
+
+	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+		return 0;
+
+	if ((ax25 = ax25_create_cb()) == NULL)
+		return 0;
+
+	ax25_fillin_cb(ax25, ax25_dev);
+
+	ax25->source_addr = *src;
+	ax25->dest_addr   = *dest;
+
+	if (digi != NULL) {
+		if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+			ax25_free_cb(ax25);
+			return 0;
+		}
+		*ax25->digipeat = *digi;
+	} else {
+		ax25_rt_build_path(ax25, dest, dev);
+	}
+
+	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+		case AX25_PROTO_STD:
+			ax25_std_establish_data_link(ax25);
+			break;
+#ifdef AX25_CONFIG_DAMA_SLAVE
+		case AX25_PROTO_DAMA_SLAVE:
+			ax25_ds_establish_data_link(ax25);
+			break;
+#endif
+	}
+
+	/* idle timeouts only for mode vc connections */
+
+	ax25->idletimer = ax25->idle;
+
+	ax25_insert_socket(ax25);
+
+	ax25->state = AX25_STATE_1;
+
+	ax25_set_timer(ax25);
+
+	ax25_output(ax25, paclen, skb);
+
+	return 1;			/* We had to create it */
+}
+
 /*
  *	All outgoing AX.25 I frames pass via this routine. Therefore this is
  *	where the fragmentation of frames takes place. If fragment is set to
  *	zero then we are not allowed to do fragmentation, even if the frame
  *	is too large.
  */
-void ax25_output(ax25_cb *ax25, int fragment, struct sk_buff *skb)
+void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
 {
 	struct sk_buff *skbn;
 	unsigned char *p;
-	int frontlen, mtu, len, fragno, ka9qfrag, first = 1;
+	int frontlen, len, fragno, ka9qfrag, first = 1;
 	long flags;
 
-	mtu = ax25->paclen;
-
-	if ((skb->len - 1) > mtu && fragment) {
+	if ((skb->len - 1) > paclen) {
 		if (*skb->data == AX25_P_TEXT) {
 			skb_pull(skb, 1); /* skip PID */
 			ka9qfrag = 0;
 		} else {
-			mtu -= 2;	/* Allow for fragment control info */
+			paclen -= 2;	/* Allow for fragment control info */
 			ka9qfrag = 1;
 		}
 
-		fragno = skb->len / mtu;
-		if (skb->len % mtu == 0) fragno--;
+		fragno = skb->len / paclen;
+		if (skb->len % paclen == 0) fragno--;
 
 		frontlen = skb_headroom(skb);	/* Address space + CTRL */
 
@@ -87,7 +150,7 @@
 			save_flags(flags); 
 			cli();
 
-			if ((skbn = alloc_skb(mtu + 2 + frontlen, GFP_ATOMIC)) == NULL) {
+			if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) {
 				restore_flags(flags);
 				printk(KERN_DEBUG "ax25_output: alloc_skb returned NULL\n");
 				return;
@@ -98,7 +161,7 @@
 			
 			restore_flags(flags);
 			
-			len = (mtu > skb->len) ? skb->len : mtu;
+			len = (paclen > skb->len) ? skb->len : paclen;
 
 			if (ka9qfrag == 1) {
 				skb_reserve(skbn, frontlen + 2);
@@ -129,9 +192,9 @@
 		skb_queue_tail(&ax25->write_queue, skb);	  /* Throw it on the queue */
 	}
 
-	if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
-		if (!ax25->dama_slave)		/* bke 960114: we aren't allowed to transmit */
-			ax25_kick(ax25);	/* in DAMA mode unless we received a Poll */
+	if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD) {
+		if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4)
+			ax25_kick(ax25);
 	}
 }
 
@@ -210,7 +273,16 @@
 			 * bke 960114: do not set the Poll bit on the last frame
 			 * in DAMA mode.
 			 */
-			ax25_send_iframe(ax25, skbn, (last && !ax25->dama_slave) ? AX25_POLLON : AX25_POLLOFF);
+			switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+				case AX25_PROTO_STD:
+					ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
+					break;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+				case AX25_PROTO_DAMA_SLAVE:
+					ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
+					break;
+#endif
+			}
 
 			ax25->vs = next;
 
@@ -236,7 +308,7 @@
 {
 	unsigned char *ptr;
 
-	if (ax25->device == NULL) {
+	if (ax25->ax25_dev == NULL) {
 		if (ax25->sk != NULL) {
 			ax25->sk->state     = TCP_CLOSE;
 			ax25->sk->err       = ENETUNREACH;
@@ -248,76 +320,44 @@
 		return;
 	}
 
-	if (skb_headroom(skb) < size_ax25_addr(ax25->digipeat)) {
+	if (skb_headroom(skb) < ax25_addr_size(ax25->digipeat)) {
 		printk(KERN_CRIT "ax25_transmit_buffer: not enough room for digi-peaters\n");
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
 
-	ptr = skb_push(skb, size_ax25_addr(ax25->digipeat));
-	build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
+	ptr = skb_push(skb, ax25_addr_size(ax25->digipeat));
+	ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
 
-	skb->dev      = ax25->device;
+	skb->dev      = ax25->ax25_dev->dev;
 	skb->priority = SOPRI_NORMAL;
 
 	ax25_queue_xmit(skb);
 }
 
 /*
- * The following routines are taken from page 170 of the 7th ARRL Computer
- * Networking Conference paper, as is the whole state machine.
+ *	A small shim to dev_queue_xmit to add the KISS control byte, and do
+ *	any packet forwarding in operation.
  */
-
-void ax25_nr_error_recovery(ax25_cb *ax25)
-{
-	ax25_establish_data_link(ax25);
-}
-
-void ax25_establish_data_link(ax25_cb *ax25)
-{
-	ax25->condition = 0x00;
-	ax25->n2count   = 0;
-
-	if (ax25->modulus == AX25_MODULUS)
-		ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
-	else
-		ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
-
-	ax25->t3timer = 0;
-	ax25->t2timer = 0;
-	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-}
-
-void ax25_transmit_enquiry(ax25_cb *ax25)
+void ax25_queue_xmit(struct sk_buff *skb)
 {
-	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
-		ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND);
-	else
-		ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND);
-
-	ax25->condition &= ~AX25_COND_ACK_PENDING;
+	unsigned char *ptr;
 
-	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-}
- 	
-void ax25_enquiry_response(ax25_cb *ax25)
-{
-	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
-		ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE);
-	else
-		ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE);
+#ifdef CONFIG_FIREWALL
+	if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
+		dev_kfree_skb(skb, FREE_WRITE);
+		return;
+	}
+#endif
 
-	ax25->condition &= ~AX25_COND_ACK_PENDING;
-}
+	skb->protocol = htons(ETH_P_AX25);
+	skb->dev      = ax25_fwd_dev(skb->dev);
+	skb->arp      = 1;
 
-void ax25_timeout_response(ax25_cb *ax25)
-{
-	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
-		ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE);
-	else
-		ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
+	ptr  = skb_push(skb, 1);
+	*ptr = 0x00;			/* KISS */
 
-	ax25->condition &= ~AX25_COND_ACK_PENDING;
+	dev_queue_xmit(skb);
 }
 
 void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
@@ -333,97 +373,6 @@
 			ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 		}
 	}
-}
-
-/*
- *	dl1bke 960114: shouldn't ax25/dama_check_need_response reside as
- *                static inline void ...() in ax25.h, should it? ;-)
- */
-void ax25_check_need_response(ax25_cb *ax25, int type, int pf)
-{
-	if (!ax25->dama_slave && type == AX25_COMMAND && pf)
-		ax25_enquiry_response(ax25);
-}
-
-/*
- *	dl1bke 960114: transmit I frames on DAMA poll
- */
-void dama_enquiry_response(ax25_cb *ax25)
-{
-	ax25_cb *ax25o;
-
-	if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) {
-		ax25_requeue_frames(ax25);
-		ax25_kick(ax25);
-	}
-
-	if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL)
-		ax25_t1_timeout(ax25);
-	else
-		ax25->n2count = 0;
-
-	ax25->t3timer = ax25->t3;
-
-	/* The FLEXNET DAMA master implementation refuses to send us ANY */
-	/* I frame for this connection if we send a REJ here, probably   */
-	/* due to its frame collector scheme? A simple RR or  RNR will   */
-	/* invoke the retransmission, and in fact REJs are superfluous   */
-	/* in DAMA mode anyway...					 */
-
-#if 0
-	if (ax25->condition & AX25_COND_REJECT)
-		ax25_send_control(ax25, AX25_REJ, AX25_POLLOFF, AX25_RESPONSE);
-	else
-#endif
-		ax25_enquiry_response(ax25);
-
-	/* Note that above response to the poll could be sent behind the  */
-	/* transmissions of the other channels as well... This version    */	
-	/* gives better performance on FLEXNET nodes. (Why, Gunter?)	  */
-
-	for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
-		if (ax25o == ax25)
-			continue;
-
-		if (ax25o->device != ax25->device)
-			continue;
-
-		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) {
-			ax25_t1_timeout(ax25o);
-			continue;
-		}
-
-		if (!ax25o->dama_slave)
-			continue;
-
-		if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && 
-		    (ax25o->state == AX25_STATE_3 || 
-		    (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) {
-			ax25_requeue_frames(ax25o);
-			ax25_kick(ax25o);
-		}
-
-		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
-			ax25_t1_timeout(ax25o);
-
-		ax25o->t3timer = ax25o->t3;
-	}
-}
-
-void dama_check_need_response(ax25_cb *ax25, int type, int pf)
-{
-	if (ax25->dama_slave && type == AX25_COMMAND && pf)
-		dama_enquiry_response(ax25);
-}
-
-void dama_establish_data_link(ax25_cb *ax25)
-{
-	ax25->condition = 0x00;
-	ax25->n2count   = 0;
-
-	ax25->t3timer = ax25->t3;
-	ax25->t2timer = 0;
-	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 }
 
 #endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v2.1.26/linux/net/ax25/ax25_route.c	Sun Feb  2 05:18:48 1997
+++ linux/net/ax25/ax25_route.c	Tue Feb 25 17:12:51 1997
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 035
+ *	AX.25 release 036
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -67,22 +67,9 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 
-static struct ax25_route {
-	struct ax25_route *next;
-	ax25_address callsign;
-	struct device *dev;
-	ax25_digi *digipeat;
-	char ip_mode;
-} *ax25_route = NULL;
-
-struct ax25_dev ax25_device[AX25_MAX_DEVICES] = {
-	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
-	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
-	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
-	{"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}
-};
+static ax25_route *ax25_route_list = NULL;
 
-static struct ax25_route *ax25_find_route(ax25_address *, struct device *);
+static ax25_route *ax25_find_route(ax25_address *, struct device *);
 
 /*
  * small macro to drop non-digipeated digipeaters and reverse path
@@ -102,25 +89,25 @@
 
 void ax25_rt_device_down(struct device *dev)
 {
-	struct ax25_route *s, *t, *ax25_rt = ax25_route;
+	ax25_route *s, *t, *ax25_rt = ax25_route_list;
 	
 	while (ax25_rt != NULL) {
 		s       = ax25_rt;
 		ax25_rt = ax25_rt->next;
 
 		if (s->dev == dev) {
-			if (ax25_route == s) {
-				ax25_route = s->next;
+			if (ax25_route_list == s) {
+				ax25_route_list = s->next;
 				if (s->digipeat != NULL)
-					kfree_s((void *)s->digipeat, sizeof(ax25_digi));
-				kfree_s((void *)s, (sizeof *s));
+					kfree_s(s->digipeat, sizeof(ax25_digi));
+				kfree_s(s, sizeof(ax25_route));
 			} else {
-				for (t = ax25_route; t != NULL; t = t->next) {
+				for (t = ax25_route_list; t != NULL; t = t->next) {
 					if (t->next == s) {
 						t->next = s->next;
 						if (s->digipeat != NULL)
-							kfree_s((void *)s->digipeat, sizeof(ax25_digi));
-						kfree_s((void *)s, sizeof(*s));
+							kfree_s(s->digipeat, sizeof(ax25_digi));
+						kfree_s(s, sizeof(ax25_route));
 						break;
 					}
 				}
@@ -132,10 +119,10 @@
 int ax25_rt_ioctl(unsigned int cmd, void *arg)
 {
 	unsigned long flags;
-	struct ax25_route *s, *t, *ax25_rt;
+	ax25_route *s, *t, *ax25_rt;
 	struct ax25_routes_struct route;
 	struct ax25_route_opt_struct rt_option;
-	struct device *dev;
+	ax25_dev *ax25_dev;
 	int i, err;
 
 	switch (cmd) {
@@ -143,12 +130,12 @@
 			if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
 				return err;		
 			copy_from_user(&route, arg, sizeof(route));
-			if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
+			if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
 				return -EINVAL;
 			if (route.digi_count > AX25_MAX_DIGIS)
 				return -EINVAL;
-			for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
-				if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == dev) {
+			for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+				if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) {
 					if (ax25_rt->digipeat != NULL) {
 						kfree_s(ax25_rt->digipeat, sizeof(ax25_digi));
 						ax25_rt->digipeat = NULL;
@@ -166,15 +153,15 @@
 					return 0;
 				}
 			}
-			if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL)
+			if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL)
 				return -ENOMEM;
 			ax25_rt->callsign     = route.dest_addr;
-			ax25_rt->dev          = dev;
+			ax25_rt->dev          = ax25_dev->dev;
 			ax25_rt->digipeat     = NULL;
 			ax25_rt->ip_mode      = ' ';
 			if (route.digi_count != 0) {
 				if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-					kfree_s(ax25_rt, sizeof(struct ax25_route));
+					kfree_s(ax25_rt, sizeof(ax25_route));
 					return -ENOMEM;
 				}
 				ax25_rt->digipeat->lastrepeat = -1;
@@ -184,10 +171,9 @@
 					ax25_rt->digipeat->calls[i]    = route.digi_addr[i];
 				}
 			}
-			save_flags(flags);
-			cli();
-			ax25_rt->next = ax25_route;
-			ax25_route    = ax25_rt;
+			save_flags(flags); cli();
+			ax25_rt->next   = ax25_route_list;
+			ax25_route_list = ax25_rt;
 			restore_flags(flags);
 			break;
 
@@ -195,25 +181,25 @@
 			if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
 				return err;
 			copy_from_user(&route, arg, sizeof(route));
-			if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
+			if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
 				return -EINVAL;
-			ax25_rt = ax25_route;
+			ax25_rt = ax25_route_list;
 			while (ax25_rt != NULL) {
 				s       = ax25_rt;
 				ax25_rt = ax25_rt->next;
-				if (s->dev == dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
-					if (ax25_route == s) {
-						ax25_route = s->next;
+				if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
+					if (ax25_route_list == s) {
+						ax25_route_list = s->next;
 						if (s->digipeat != NULL)
-							kfree_s((void *)s->digipeat, sizeof(ax25_digi));
-						kfree_s((void *)s, (sizeof *s));
+							kfree_s(s->digipeat, sizeof(ax25_digi));
+						kfree_s(s, sizeof(ax25_route));
 					} else {
-						for (t = ax25_route; t != NULL; t = t->next) {
+						for (t = ax25_route_list; t != NULL; t = t->next) {
 							if (t->next == s) {
 								t->next = s->next;
 								if (s->digipeat != NULL)
-									kfree_s((void *)s->digipeat, sizeof(ax25_digi));
-								kfree_s((void *)s, sizeof(*s));
+									kfree_s(s->digipeat, sizeof(ax25_digi));
+								kfree_s(s, sizeof(ax25_route));
 								break;
 							}
 						}				
@@ -226,10 +212,10 @@
 			if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0)
 				return err;
 			copy_from_user(&rt_option, arg, sizeof(rt_option));
-			if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL)
+			if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL)
 				return -EINVAL;
-			for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
-				if (ax25_rt->dev == dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
+			for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+				if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
 					switch (rt_option.cmd) {
 						case AX25_SET_RT_IPMODE:
 							switch (rt_option.arg) {
@@ -258,7 +244,7 @@
 
 int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
-	struct ax25_route *ax25_rt;
+	ax25_route *ax25_rt;
 	int len     = 0;
 	off_t pos   = 0;
 	off_t begin = 0;
@@ -269,7 +255,7 @@
 
 	len += sprintf(buffer, "callsign  dev  mode digipeaters\n");
 
-	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+	for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
 		if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
 			callsign = "default";
 		else
@@ -317,55 +303,20 @@
 	return len;
 } 
 
-int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	ax25_uid_assoc *pt;
-	int len     = 0;
-	off_t pos   = 0;
-	off_t begin = 0;
-
-	cli();
-
-	len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
-
-	for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
-		len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
-
-		pos = begin + len;
-
-		if (pos < offset) {
-			len = 0;
-			begin = pos;
-		}
-
-		if (pos > offset + length)
-			break;
-	}
-
-	sti();
-
-	*start = buffer + (offset - begin);
-	len   -= offset - begin;
-
-	if (len > length) len = length;
-
-	return len;
-}
-
 /*
  *	Find AX.25 route
  */
-static struct ax25_route *ax25_find_route(ax25_address *addr, struct device *dev)
+static ax25_route *ax25_find_route(ax25_address *addr, struct device *dev)
 {
-	struct ax25_route *ax25_spe_rt = NULL;
-	struct ax25_route *ax25_def_rt = NULL;
-	struct ax25_route *ax25_rt;
+	ax25_route *ax25_spe_rt = NULL;
+	ax25_route *ax25_def_rt = NULL;
+	ax25_route *ax25_rt;
 
 	/*
 	 *	Bind to the physical interface we heard them on, or the default
 	 *	route if none is found;
 	 */
-	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+	for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
 		if (dev == NULL) {
 			if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
 				ax25_spe_rt = ax25_rt;
@@ -408,18 +359,19 @@
  */
 int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
 {
-	struct ax25_route *ax25_rt;
+	ax25_route *ax25_rt;
 	ax25_address *call;
 
 	if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL)
 		return -EHOSTUNREACH;
 
-	ax25->device = ax25_rt->dev;
+	if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
+		return -EHOSTUNREACH;
 
 	if ((call = ax25_findbyuid(current->euid)) == NULL) {
 		if (ax25_uid_policy && !suser())
 			return -EPERM;
-		call = (ax25_address *)ax25->device->dev_addr;
+		call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
 	}
 
 	ax25->source_addr = *call;
@@ -443,7 +395,7 @@
  */
 void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr, struct device *dev)
 {
-	struct ax25_route *ax25_rt;
+	ax25_route *ax25_rt;
 
 	if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
 		return;
@@ -454,14 +406,16 @@
 	if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
 		return;
 
-	ax25->device    = ax25_rt->dev;
+	if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
+		return;
+
 	*ax25->digipeat = *ax25_rt->digipeat;
 	ax25_adjust_path(addr, ax25->digipeat);
 }
 
 void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev)
 {
-	struct ax25_route *ax25_rt;
+	ax25_route *ax25_rt;
 	ax25_digi digipeat;
 	ax25_address src, dest;
 	unsigned char *bp;
@@ -491,7 +445,7 @@
 
 	bp = skb_push(skb, len);
 
-	build_ax25_addr(bp, &src, &dest, ax25_rt->digipeat, AX25_COMMAND, AX25_MODULUS);
+	ax25_addr_build(bp, &src, &dest, ax25_rt->digipeat, AX25_COMMAND, AX25_MODULUS);
 }
 
 /*
@@ -499,154 +453,23 @@
  */
 char ax25_ip_mode_get(ax25_address *callsign, struct device *dev)
 {
-	struct ax25_route *ax25_rt;
+	ax25_route *ax25_rt;
 
-	for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next)
+	for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next)
 		if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev)
 			return ax25_rt->ip_mode;
 
 	return ' ';
 }
 
-static struct ax25_dev *ax25_dev_get_dev(struct device *dev)
-{
-	int i;
-
-	for (i = 0; i < AX25_MAX_DEVICES; i++)
-		if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev)
-			return ax25_device + i;
-
-	return NULL;
-}
-
-/*
- *	Wow, a bit of data hiding. Is this C++ or what ?
- */
-int ax25_dev_get_value(struct device *dev, int valueno)
-{
-	struct ax25_dev *ax25_dev;
-
-	if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) {
-		printk(KERN_WARNING "ax25_dev_get_value called with invalid device\n");
-		return 1;
-	}
-
-	return ax25_dev->values[valueno];
-}
-
-/*
- *	This is called when an interface is brought up. These are
- *	reasonable defaults.
- */
-void ax25_dev_device_up(struct device *dev)
-{
-	struct ax25_dev *ax25_dev = NULL;
-	int i;
-
-	for (i = 0; i < AX25_MAX_DEVICES; i++) {
-		if (ax25_device[i].dev == NULL) {
-			ax25_dev = ax25_device + i;
-			break;
-		}
-	}
-
-	if (ax25_dev == NULL) {
-		printk(KERN_ERR "ax25_dev_device_up cannot find free AX.25 device\n");
-		return;
-	}
-
-	ax25_unregister_sysctl();
-
-	sprintf(ax25_dev->name, "%s.parms", dev->name);
-
-	ax25_dev->dev     = dev;
-	ax25_dev->forward = NULL;
-
-	ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
-	ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
-	ax25_dev->values[AX25_VALUES_TEXT]      = AX25_DEF_TEXT;
-	ax25_dev->values[AX25_VALUES_BACKOFF]   = AX25_DEF_BACKOFF;
-	ax25_dev->values[AX25_VALUES_CONMODE]   = AX25_DEF_CONMODE;
-	ax25_dev->values[AX25_VALUES_WINDOW]    = AX25_DEF_WINDOW;
-	ax25_dev->values[AX25_VALUES_EWINDOW]   = AX25_DEF_EWINDOW;
-	ax25_dev->values[AX25_VALUES_T1]        = AX25_DEF_T1;
-	ax25_dev->values[AX25_VALUES_T2]        = AX25_DEF_T2;
-	ax25_dev->values[AX25_VALUES_T3]        = AX25_DEF_T3;
-	ax25_dev->values[AX25_VALUES_IDLE]	= AX25_DEF_IDLE;
-	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
-	ax25_dev->values[AX25_VALUES_DIGI]      = AX25_DEF_DIGI;
-	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
-	ax25_dev->values[AX25_VALUES_MAXQUEUE]	= AX25_DEF_MAXQUEUE;
-
-	ax25_register_sysctl();
-}
-
-void ax25_dev_device_down(struct device *dev)
-{
-	struct ax25_dev *ax25_dev;
-
-	ax25_unregister_sysctl();
-
-	if ((ax25_dev = ax25_dev_get_dev(dev)) != NULL)
-		ax25_dev->dev = NULL;
-
-	ax25_register_sysctl();
-}
-
-int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
-{
-	struct device *dev;
-	struct ax25_dev *ax25_dev;
-
-	if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL)
-		return -EINVAL;
-
-	if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
-		return -EINVAL;
-
-	switch (cmd) {
-		case SIOCAX25ADDFWD:
-			if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL)
-				return -EINVAL;
-			if (ax25_dev->forward != NULL)
-				return -EINVAL;
-			ax25_dev->forward = dev;
-			break;
-
-		case SIOCAX25DELFWD:
-			if (ax25_dev->forward == NULL)
-				return -EINVAL;
-			ax25_dev->forward = NULL;
-			break;
-
-		default:
-			return -EINVAL;
-	}
-
-	return 0;
-}
-
-struct device *ax25_fwd_dev(struct device *dev)
-{
-	struct ax25_dev *ax25_dev;
-
-	if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
-		return dev;
-
-	if (ax25_dev->forward == NULL)
-		return dev;
-
-	return ax25_dev->forward;
-}
-
 #ifdef MODULE
 
 /*
- *	Free all memory associated with routing and device structures.
+ *	Free all memory associated with routing structures.
  */
 void ax25_rt_free(void)
 {
-	struct ax25_route *s, *ax25_rt = ax25_route;
+	ax25_route *s, *ax25_rt = ax25_route_list;
 
 	while (ax25_rt != NULL) {
 		s       = ax25_rt;
@@ -655,7 +478,7 @@
 		if (s->digipeat != NULL)
 			kfree_s(s->digipeat, sizeof(ax25_digi));
 
-		kfree_s(s, sizeof(struct ax25_route));
+		kfree_s(s, sizeof(ax25_route));
 	}
 }
 
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_std_in.c linux/net/ax25/ax25_std_in.c
--- v2.1.26/linux/net/ax25/ax25_std_in.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_std_in.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,542 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	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.
+ *
+ *	Most of this code is based on the SDL diagrams published in the 7th
+ *	ARRL Computer Networking Conference papers. The diagrams have mistakes
+ *	in them, but are mostly correct. Before you modify the code could you
+ *	read the SDL diagrams as the code is not obvious and probably very
+ *	easy to break;
+ *
+ *	History
+ *	AX.25 028a	Jonathan(G4KLX)	New state machine based on SDL diagrams.
+ *	AX.25 028b	Jonathan(G4KLX) Extracted AX25 control block from
+ *					the sock structure.
+ *	AX.25 029	Alan(GW4PTS)	Switched to KA9Q constant names.
+ *			Jonathan(G4KLX)	Added IP mode registration.
+ *	AX.25 030	Jonathan(G4KLX)	Added AX.25 fragment reception.
+ *					Upgraded state machine for SABME.
+ *					Added arbitrary protocol id support.
+ *	AX.25 031	Joerg(DL1BKE)	Added DAMA support
+ *			HaJo(DD8NE)	Added Idle Disc Timer T5
+ *			Joerg(DL1BKE)   Renamed it to "IDLE" with a slightly
+ *					different behaviour. Fixed defrag
+ *					routine (I hope)
+ *	AX.25 032	Darryl(G7LED)	AX.25 segmentation fixed.
+ *	AX.25 033	Jonathan(G4KLX)	Remove auto-router.
+ *					Modularisation changes.
+ *	AX.25 035	Hans(PE1AYX)	Fixed interface to IP layer.
+ *	AX.25 036	Jonathan(G4KLX)	Cloned from ax25_in.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h>			/* For ip_rcv */
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ *	State machine for state 1, Awaiting Connection State.
+ *	The handling of the timer(s) is in file ax25_std_timer.c.
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+	switch (frametype) {
+		case AX25_SABM:
+			ax25->modulus = AX25_MODULUS;
+			ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			break;
+
+		case AX25_SABME:
+			ax25->modulus = AX25_EMODULUS;
+			ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			break;
+
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
+			break;
+
+		case AX25_UA:
+			if (pf) {
+				ax25_calculate_rtt(ax25);
+				ax25->t1timer = 0;
+				ax25->t3timer = ax25->t3;
+				ax25->idletimer = ax25->idle;
+				ax25->vs      = 0;
+				ax25->va      = 0;
+				ax25->vr      = 0;
+				ax25->state   = AX25_STATE_3;
+				ax25->n2count = 0;
+				if (ax25->sk != NULL) {
+					ax25->sk->state = TCP_ESTABLISHED;
+					/* For WAIT_SABM connections we will produce an accept ready socket here */
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+				}
+			}
+			break;
+
+		case AX25_DM:
+			if (pf) {
+				if (ax25->modulus == AX25_MODULUS) {
+					ax25_clear_queues(ax25);
+					ax25->state = AX25_STATE_0;
+					if (ax25->sk != NULL) {
+						ax25->sk->state     = TCP_CLOSE;
+						ax25->sk->err       = ECONNREFUSED;
+						ax25->sk->shutdown |= SEND_SHUTDOWN;
+						if (!ax25->sk->dead)
+							ax25->sk->state_change(ax25->sk);
+						ax25->sk->dead      = 1;
+					}
+				} else {
+					ax25->modulus = AX25_MODULUS;
+					ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+				}
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ *	State machine for state 2, Awaiting Release State.
+ *	The handling of the timer(s) is in file ax25_std_timer.c
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+	switch (frametype) {
+		case AX25_SABM:
+		case AX25_SABME:
+			ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
+			break;
+
+		case AX25_DISC:
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->state = AX25_STATE_0;
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = 0;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_DM:
+		case AX25_UA:
+			if (pf) {
+				ax25->state = AX25_STATE_0;
+				if (ax25->sk != NULL) {
+					ax25->sk->state     = TCP_CLOSE;
+					ax25->sk->err       = 0;
+					ax25->sk->shutdown |= SEND_SHUTDOWN;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead      = 1;
+				}
+			}
+			break;
+
+		case AX25_I:
+		case AX25_REJ:
+		case AX25_RNR:
+		case AX25_RR:
+			if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+			break;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+
+/*
+ *	State machine for state 3, Connected State.
+ *	The handling of the timer(s) is in file ax25_std_timer.c
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+	int queued = 0;
+
+	switch (frametype) {
+		case AX25_SABM:
+		case AX25_SABME:
+			if (frametype == AX25_SABM) {
+				ax25->modulus = AX25_MODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+			} else {
+				ax25->modulus = AX25_EMODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+			}
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->condition = 0x00;
+			ax25->t1timer   = 0;
+			ax25->t3timer   = ax25->t3;
+			ax25->idletimer = ax25->idle;
+			ax25->vs        = 0;
+			ax25->va        = 0;
+			ax25->vr        = 0;
+			ax25_requeue_frames(ax25);
+			break;
+
+		case AX25_DISC:
+			ax25_clear_queues(ax25);
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = 0;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_DM:
+			ax25_clear_queues(ax25);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = ECONNRESET;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_RR:
+		case AX25_RNR:
+			if (frametype == AX25_RR)
+				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			else
+				ax25->condition |= AX25_COND_PEER_RX_BUSY;
+			if (type == AX25_COMMAND && pf)
+				ax25_std_enquiry_response(ax25);
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_check_iframes_acked(ax25, nr);
+			} else {
+				ax25_std_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_REJ:
+			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			if (type == AX25_COMMAND && pf)
+				ax25_std_enquiry_response(ax25);
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_frames_acked(ax25, nr);
+				ax25_calculate_rtt(ax25);
+				ax25->t1timer = 0;
+				ax25->t3timer = ax25->t3;
+				ax25_requeue_frames(ax25);
+			} else {
+				ax25_std_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_I:
+			if (!ax25_validate_nr(ax25, nr)) {
+				ax25_std_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+				break;
+			}
+			if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
+				ax25_frames_acked(ax25, nr);
+			} else {
+				ax25_check_iframes_acked(ax25, nr);
+			}
+			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+				if (pf) ax25_std_enquiry_response(ax25);
+				break;
+			}
+			if (ns == ax25->vr) {
+				ax25->vr = (ax25->vr + 1) % ax25->modulus;
+				queued = ax25_rx_iframe(ax25, skb);
+				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+					ax25->vr = ns;	/* ax25->vr - 1 */
+					if (pf) ax25_std_enquiry_response(ax25);
+					break;
+				}
+				ax25->condition &= ~AX25_COND_REJECT;
+				if (pf) {
+					ax25_std_enquiry_response(ax25);
+				} else {
+					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+						ax25->t2timer = ax25->t2;
+						ax25->condition |= AX25_COND_ACK_PENDING;
+					}
+				}
+			} else {
+				if (ax25->condition & AX25_COND_REJECT) {
+					if (pf) ax25_std_enquiry_response(ax25);
+				} else {
+					ax25->condition |= AX25_COND_REJECT;
+					ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
+				}
+			}
+			break;
+
+		case AX25_FRMR:
+		case AX25_ILLEGAL:
+			ax25_std_establish_data_link(ax25);
+			ax25->state = AX25_STATE_1;
+			break;
+
+		default:
+			break;
+	}
+
+	return queued;
+}
+
+/*
+ *	State machine for state 4, Timer Recovery State.
+ *	The handling of the timer(s) is in file ax25_std_timer.c
+ *	Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+	int queued = 0;
+
+	switch (frametype) {
+		case AX25_SABM:
+		case AX25_SABME:
+			if (frametype == AX25_SABM) {
+				ax25->modulus = AX25_MODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+			} else {
+				ax25->modulus = AX25_EMODULUS;
+				ax25->window  = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+			}
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->condition = 0x00;
+			ax25->t1timer   = 0;
+			ax25->t3timer   = ax25->t3;
+			ax25->idletimer = ax25->idle;
+			ax25->vs        = 0;
+			ax25->va        = 0;
+			ax25->vr        = 0;
+			ax25->state     = AX25_STATE_3;
+			ax25->n2count   = 0;
+			ax25_requeue_frames(ax25);
+			break;
+
+		case AX25_DISC:
+			ax25_clear_queues(ax25);
+			ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = 0;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_DM:
+			ax25_clear_queues(ax25);
+			ax25->t3timer = 0;
+			ax25->state   = AX25_STATE_0;
+			if (ax25->sk != NULL) {
+				ax25->sk->state     = TCP_CLOSE;
+				ax25->sk->err       = ECONNRESET;
+				ax25->sk->shutdown |= SEND_SHUTDOWN;
+				if (!ax25->sk->dead)
+					ax25->sk->state_change(ax25->sk);
+				ax25->sk->dead      = 1;
+			}
+			break;
+
+		case AX25_RR:
+		case AX25_RNR:
+			if (frametype == AX25_RR)
+				ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			else
+				ax25->condition |= AX25_COND_PEER_RX_BUSY;
+			if (type == AX25_RESPONSE && pf) {
+				ax25->t1timer = 0;
+				if (ax25_validate_nr(ax25, nr)) {
+					ax25_frames_acked(ax25, nr);
+					if (ax25->vs == ax25->va) {
+						ax25->t3timer = ax25->t3;
+						ax25->n2count = 0;
+						ax25->state   = AX25_STATE_3;
+					} else {
+						ax25_requeue_frames(ax25);
+					}
+				} else {
+					ax25_std_nr_error_recovery(ax25);
+					ax25->state = AX25_STATE_1;
+				}
+				break;
+			}
+			if (type == AX25_COMMAND && pf)
+				ax25_std_enquiry_response(ax25);
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_frames_acked(ax25, nr);
+			} else {
+				ax25_std_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_REJ:
+			ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+			if (pf && type == AX25_RESPONSE) {
+				ax25->t1timer = 0;
+				if (ax25_validate_nr(ax25, nr)) {
+					ax25_frames_acked(ax25, nr);
+					if (ax25->vs == ax25->va) {
+						ax25->t3timer = ax25->t3;
+						ax25->n2count = 0;
+						ax25->state   = AX25_STATE_3;
+					} else {
+						ax25_requeue_frames(ax25);
+					}
+				} else {
+					ax25_std_nr_error_recovery(ax25);
+					ax25->state = AX25_STATE_1;
+				}
+				break;
+			}
+			if (type == AX25_COMMAND && pf)
+				ax25_std_enquiry_response(ax25);
+			if (ax25_validate_nr(ax25, nr)) {
+				ax25_frames_acked(ax25, nr);
+				ax25_requeue_frames(ax25);
+			} else {
+				ax25_std_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+			}
+			break;
+
+		case AX25_I:
+			if (!ax25_validate_nr(ax25, nr)) {
+				ax25_std_nr_error_recovery(ax25);
+				ax25->state = AX25_STATE_1;
+				break;
+			}
+			ax25_frames_acked(ax25, nr);
+			if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+				if (pf) ax25_std_enquiry_response(ax25);
+				break;
+			}
+			if (ns == ax25->vr) {
+				ax25->vr = (ax25->vr + 1) % ax25->modulus;
+				queued = ax25_rx_iframe(ax25, skb);
+				if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+					ax25->vr = ns;	/* ax25->vr - 1 */
+					if (pf) ax25_std_enquiry_response(ax25);
+					break;
+				}
+				ax25->condition &= ~AX25_COND_REJECT;
+				if (pf) {
+					ax25_std_enquiry_response(ax25);
+				} else {
+					if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+						ax25->t2timer = ax25->t2;
+						ax25->condition |= AX25_COND_ACK_PENDING;
+					}
+				}
+			} else {
+				if (ax25->condition & AX25_COND_REJECT) {
+					if (pf) ax25_std_enquiry_response(ax25);
+				} else {
+					ax25->condition |= AX25_COND_REJECT;
+					ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
+				}
+			}
+			break;
+
+		case AX25_FRMR:
+		case AX25_ILLEGAL:
+			ax25_std_establish_data_link(ax25);
+			ax25->state = AX25_STATE_1;
+			break;
+
+		default:
+			break;
+	}
+
+	return queued;
+}
+
+/*
+ *	Higher level upcall for a LAPB frame
+ */
+int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
+{
+	int queued = 0, frametype, ns, nr, pf;
+
+	frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
+
+	switch (ax25->state) {
+		case AX25_STATE_1:
+			queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type);
+			break;
+		case AX25_STATE_2:
+			queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type);
+			break;
+		case AX25_STATE_3:
+			queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
+			break;
+		case AX25_STATE_4:
+			queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
+			break;
+	}
+
+	return queued;
+}
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_std_subr.c linux/net/ax25/ax25_std_subr.c
--- v2.1.26/linux/net/ax25/ax25_std_subr.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_std_subr.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,105 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	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.
+ *
+ *	Most of this code is based on the SDL diagrams published in the 7th
+ *	ARRL Computer Networking Conference papers. The diagrams have mistakes
+ *	in them, but are mostly correct. Before you modify the code could you
+ *	read the SDL diagrams as the code is not obvious and probably very
+ *	easy to break;
+ *
+ *	History
+ *	AX.25 036	Jonathan(G4KLX)	Split from ax25_out.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ * The following routines are taken from page 170 of the 7th ARRL Computer
+ * Networking Conference paper, as is the whole state machine.
+ */
+
+void ax25_std_nr_error_recovery(ax25_cb *ax25)
+{
+	ax25_std_establish_data_link(ax25);
+}
+
+void ax25_std_establish_data_link(ax25_cb *ax25)
+{
+	ax25->condition = 0x00;
+	ax25->n2count   = 0;
+
+	if (ax25->modulus == AX25_MODULUS)
+		ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+	else
+		ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
+
+	ax25->t3timer = 0;
+	ax25->t2timer = 0;
+	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+}
+
+void ax25_std_transmit_enquiry(ax25_cb *ax25)
+{
+	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+		ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND);
+	else
+		ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND);
+
+	ax25->condition &= ~AX25_COND_ACK_PENDING;
+
+	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+}
+ 	
+void ax25_std_enquiry_response(ax25_cb *ax25)
+{
+	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+		ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE);
+	else
+		ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE);
+
+	ax25->condition &= ~AX25_COND_ACK_PENDING;
+}
+
+void ax25_std_timeout_response(ax25_cb *ax25)
+{
+	if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+		ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE);
+	else
+		ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
+
+	ax25->condition &= ~AX25_COND_ACK_PENDING;
+}
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_std_timer.c linux/net/ax25/ax25_std_timer.c
--- v2.1.26/linux/net/ax25/ax25_std_timer.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_std_timer.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,217 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
+ *	releases, misbehave and/or generally screw up. It might even work. 
+ *
+ *	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
+ *	AX.25 028a	Jonathan(G4KLX)	New state machine based on SDL diagrams.
+ *	AX.25 028b	Jonathan(G4KLX)	Extracted AX25 control block from the
+ *					sock structure.
+ *	AX.25 029	Alan(GW4PTS)	Switched to KA9Q constant names.
+ *	AX.25 031	Joerg(DL1BKE)	Added DAMA support
+ *	AX.25 032	Joerg(DL1BKE)	Fixed DAMA timeout bug
+ *	AX.25 033	Jonathan(G4KLX)	Modularisation functions.
+ *	AX.25 035	Frederic(F1OAT)	Support for pseudo-digipeating.
+ *	AX.25 036	Jonathan(G4KLX)	Split from ax25_timer.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+void ax25_std_timer(ax25_cb *ax25)
+{
+	switch (ax25->state) {
+		case AX25_STATE_0:
+			/* Magic here: If we listen() and a new link dies before it
+			   is accepted() it isn't 'dead' so doesn't get removed. */
+			if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
+				del_timer(&ax25->timer);
+				ax25_destroy_socket(ax25);
+				return;
+			}
+			break;
+
+		case AX25_STATE_3:
+		case AX25_STATE_4:
+			/*
+			 * Check the state of the receive buffer.
+			 */
+			if (ax25->sk != NULL) {
+				if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
+					ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+					ax25->condition &= ~AX25_COND_ACK_PENDING;
+					ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
+					break;
+				}
+			}
+			/*
+			 * Check for frames to transmit.
+			 */
+			ax25_kick(ax25);
+			break;
+
+		default:
+			break;
+	}
+
+	if (ax25->t2timer > 0 && --ax25->t2timer == 0) {
+		if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
+			if (ax25->condition & AX25_COND_ACK_PENDING) {
+				ax25->condition &= ~AX25_COND_ACK_PENDING;
+				ax25_std_timeout_response(ax25);
+			}
+		}
+	}
+
+	if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
+		if (ax25->state == AX25_STATE_3) {
+			ax25->n2count = 0;
+			ax25_std_transmit_enquiry(ax25);
+			ax25->state   = AX25_STATE_4;
+		}
+		ax25->t3timer = ax25->t3;
+	}
+
+	if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
+		/* dl1bke 960228: close the connection when IDLE expires */
+		/* 		  similar to DAMA T3 timeout but with    */
+		/* 		  a "clean" disconnect of the connection */
+
+		ax25_clear_queues(ax25);
+
+		ax25->n2count = 0;
+		ax25->t3timer = 0;
+		ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+		ax25->state   = AX25_STATE_2;
+		ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+		if (ax25->sk != NULL) {
+			ax25->sk->state     = TCP_CLOSE;
+			ax25->sk->err       = 0;
+			ax25->sk->shutdown |= SEND_SHUTDOWN;
+			if (!ax25->sk->dead)
+				ax25->sk->state_change(ax25->sk);
+			ax25->sk->dead      = 1;
+			ax25->sk->destroy   = 1;
+		}
+	}
+
+	if (ax25->t1timer == 0 || --ax25->t1timer > 0) {
+		ax25_set_timer(ax25);
+		return;
+	}
+
+	switch (ax25->state) {
+		case AX25_STATE_1: 
+			if (ax25->n2count == ax25->n2) {
+				if (ax25->modulus == AX25_MODULUS) {
+					ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+					ax25_clear_queues(ax25);
+					ax25->state = AX25_STATE_0;
+					if (ax25->sk != NULL) {
+						ax25->sk->state     = TCP_CLOSE;
+						ax25->sk->err       = ETIMEDOUT;
+						ax25->sk->shutdown |= SEND_SHUTDOWN;
+						if (!ax25->sk->dead)
+							ax25->sk->state_change(ax25->sk);
+						ax25->sk->dead      = 1;
+					}
+				} else {
+					ax25->modulus = AX25_MODULUS;
+					ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+					ax25->n2count = 0;
+					ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+				}
+			} else {
+				ax25->n2count++;
+				if (ax25->modulus == AX25_MODULUS)
+					ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+				else
+					ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
+			}
+			break;
+
+		case AX25_STATE_2:
+			if (ax25->n2count == ax25->n2) {
+				ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+				ax25_clear_queues(ax25);
+				ax25->state = AX25_STATE_0;
+				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+
+				if (ax25->sk != NULL) {
+					ax25->sk->state     = TCP_CLOSE;
+					ax25->sk->err       = ETIMEDOUT;
+					ax25->sk->shutdown |= SEND_SHUTDOWN;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead      = 1;
+				}
+			} else {
+				ax25->n2count++;
+				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+			}
+			break;
+
+		case AX25_STATE_3: 
+			ax25->n2count = 1;
+			ax25_std_transmit_enquiry(ax25);
+			ax25->state   = AX25_STATE_4;
+			break;
+
+		case AX25_STATE_4:
+			if (ax25->n2count == ax25->n2) {
+				ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+				ax25_clear_queues(ax25);
+				ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+				ax25->state = AX25_STATE_0;
+				if (ax25->sk != NULL) {
+					SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n");
+					ax25->sk->state     = TCP_CLOSE;
+					ax25->sk->err       = ETIMEDOUT;
+					ax25->sk->shutdown |= SEND_SHUTDOWN;
+					if (!ax25->sk->dead)
+						ax25->sk->state_change(ax25->sk);
+					ax25->sk->dead      = 1;
+				}
+			} else {
+				ax25->n2count++;
+				ax25_std_transmit_enquiry(ax25);
+			}
+			break;
+	}
+
+	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+	ax25_set_timer(ax25);
+}
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v2.1.26/linux/net/ax25/ax25_subr.c	Sun Feb  2 05:18:48 1997
+++ linux/net/ax25/ax25_subr.c	Tue Feb 25 17:12:51 1997
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 035
+ *	AX.25 release 036
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -95,17 +95,10 @@
 		        skb = skb_dequeue(&ax25->ack_queue);
 			kfree_skb(skb, FREE_WRITE);
 			ax25->va = (ax25->va + 1) % ax25->modulus;
-			if (ax25->dama_slave)
-				ax25->n2count = 0;
 		}
 	}
 }
 
-/*
- * Maybe this should be your ax25_invoke_retransmission(), which appears
- * to be used but not do anything.  ax25_invoke_retransmission() used to
- * be in AX 0.29, but has now gone in 0.30.
- */
 void ax25_requeue_frames(ax25_cb *ax25)
 {
         struct sk_buff *skb, *skb_prev = NULL;
@@ -200,15 +193,11 @@
 {
 	struct sk_buff *skb;
 	unsigned char  *dptr;
-	struct device *dev;
 
-	if ((dev = ax25->device) == NULL)
-		return;	/* Route died */
-
-	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL)
+	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL)
 		return;
 
-	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat));
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat));
 
 	/* Assume a response - address structure for DTE */
 	if (ax25->modulus == AX25_MODULUS) {
@@ -247,10 +236,10 @@
 	if (dev == NULL)
 		return;
 
-	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(digi) + 1, GFP_ATOMIC)) == NULL)
+	if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(digi) + 1, GFP_ATOMIC)) == NULL)
 		return;	/* Next SABM will get DM'd */
 
-	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(digi));
+	skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(digi));
 
 	ax25_digi_invert(digi, &retdigi);
 
@@ -261,8 +250,8 @@
 	/*
 	 *	Do the address ourselves
 	 */
-	dptr  = skb_push(skb, size_ax25_addr(digi));
-	dptr += build_ax25_addr(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS);
+	dptr  = skb_push(skb, ax25_addr_size(digi));
+	dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS);
 
 	skb->dev      = dev;
 	skb->priority = SOPRI_NORMAL;
@@ -308,249 +297,6 @@
 
 	if (ax25->rtt > AX25_T1CLAMPHI)
 		ax25->rtt = AX25_T1CLAMPHI;
-}
-
-/*
- *	Digipeated address processing
- */
-
-
-/*
- *	Given an AX.25 address pull of to, from, digi list, command/response and the start of data
- *
- */
-unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
-{
-	int d = 0;
-
-	if (len < 14) return NULL;
-
-	if (flags != NULL) {
-		*flags = 0;
-
-		if (buf[6] & AX25_CBIT)
-			*flags = AX25_COMMAND;
-		if (buf[13] & AX25_CBIT)
-			*flags = AX25_RESPONSE;
-	}
-
-	if (dama != NULL) 
-		*dama = ~buf[13] & AX25_DAMA_FLAG;
-
-	/* Copy to, from */
-	if (dest != NULL) 
-		memcpy(dest, buf + 0, AX25_ADDR_LEN);
-
-	if (src != NULL)  
-		memcpy(src,  buf + 7, AX25_ADDR_LEN);
-
-	buf += 2 * AX25_ADDR_LEN;
-	len -= 2 * AX25_ADDR_LEN;
-	digi->lastrepeat = -1;
-	digi->ndigi      = 0;
-
-	while (!(buf[-1] & AX25_EBIT)) {
-		if (d >= AX25_MAX_DIGIS)  return NULL;	/* Max of 6 digis */
-		if (len < 7) return NULL;	/* Short packet */
-
-		if (digi != NULL) {
-			memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
-			digi->ndigi = d + 1;
-			if (buf[6] & AX25_HBIT) {
-				digi->repeated[d] = 1;
-				digi->lastrepeat  = d;
-			} else {
-				digi->repeated[d] = 0;
-			}
-		}
-
-		buf += AX25_ADDR_LEN;
-		len -= AX25_ADDR_LEN;
-		d++;
-	}
-
-	return buf;
-}
-
-/*
- *	Assemble an AX.25 header from the bits
- */
-int build_ax25_addr(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus)
-{
-	int len = 0;
-	int ct  = 0;
-
-	memcpy(buf, dest, AX25_ADDR_LEN);
-	buf[6] &= ~(AX25_EBIT | AX25_CBIT);
-	buf[6] |= AX25_SSSID_SPARE;
-
-	if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
-
-	buf += AX25_ADDR_LEN;
-	len += AX25_ADDR_LEN;
-
-	memcpy(buf, src, AX25_ADDR_LEN);
-	buf[6] &= ~(AX25_EBIT | AX25_CBIT);
-	buf[6] &= ~AX25_SSSID_SPARE;
-
-	if (modulus == AX25_MODULUS)
-		buf[6] |= AX25_SSSID_SPARE;
-	else
-		buf[6] |= AX25_ESSID_SPARE;
-
-	if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT;
-
-	/*
-	 *	Fast path the normal digiless path
-	 */
-	if (d == NULL || d->ndigi == 0) {
-		buf[6] |= AX25_EBIT;
-		return 2 * AX25_ADDR_LEN;
-	}	
-
-	buf += AX25_ADDR_LEN;
-	len += AX25_ADDR_LEN;
-
-	while (ct < d->ndigi) {
-		memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
-
-		if (d->repeated[ct])
-			buf[6] |= AX25_HBIT;
-		else
-			buf[6] &= ~AX25_HBIT;
-
-		buf[6] &= ~AX25_EBIT;
-		buf[6] |= AX25_SSSID_SPARE;
-
-		buf += AX25_ADDR_LEN;
-		len += AX25_ADDR_LEN;
-		ct++;
-	}
-
-	buf[-1] |= AX25_EBIT;
-
-	return len;
-}
-
-int size_ax25_addr(ax25_digi *dp)
-{
-	if (dp == NULL)
-		return 2 * AX25_ADDR_LEN;
-
-	return AX25_ADDR_LEN * (2 + dp->ndigi);
-}
-
-/* 
- *	Reverse Digipeat List. May not pass both parameters as same struct
- */
-void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
-{
-	int ct = 0;
-
-	out->ndigi      = in->ndigi;
-	out->lastrepeat = in->ndigi - in->lastrepeat - 2;
-
-	/* Invert the digipeaters */
-
-	while (ct < in->ndigi) {
-		out->calls[ct]    = in->calls[in->ndigi - ct - 1];
-		if (ct <= out->lastrepeat) {
-			out->calls[ct].ax25_call[6] |= AX25_HBIT;
-			out->repeated[ct]            = 1;
-		} else {
-			out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
-			out->repeated[ct]            = 0;
-		}
-		ct++;
-	}
-}
-
-/*
- *	count the number of buffers on a list belonging to the same
- *	socket as skb
- */
-static int ax25_list_length(struct sk_buff_head *list, struct sk_buff *skb)
-{
-	int count = 0;
-	long flags;
-	struct sk_buff *skbq;
-
-	save_flags(flags);
-	cli();
-
-	if (list == NULL) {
-		restore_flags(flags);
-                return 0;
-        }
-
-	for (skbq = list->next; skbq != (struct sk_buff *)list; skbq = skbq->next)
-		if (skb->sk == skbq->sk)
-			count++;
-
-        restore_flags(flags);
-
-        return count;
-}
-
-/*
- *	count the number of buffers of one socket on the write/ack-queue
- */
-int ax25_queue_length(ax25_cb *ax25, struct sk_buff *skb)
-{
-	return ax25_list_length(&ax25->write_queue, skb) + ax25_list_length(&ax25->ack_queue, skb);
-}
-
-/*
- *	:::FIXME:::
- *	This is ****NOT**** the right approach. Not all drivers do kiss. We
- *	need a driver level request to switch duplex mode, that does either
- *	SCC changing, PI config or KISS as required.
- *
- *	Not to mention this request isn't currently reliable.
- */
-void ax25_kiss_cmd(ax25_cb *ax25, unsigned char cmd, unsigned char param)
-{
-	struct sk_buff *skb;
-	unsigned char *p;
-
-	if (ax25->device == NULL)
-		return;
-
-	if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
-		return;
-
-	p = skb_put(skb, 2);
-
-	*p++ = cmd;
-	*p++ = param;
-
-	skb->arp      = 1;
-	skb->dev      = ax25->device;
-	skb->priority = SOPRI_NORMAL;
-	skb->protocol = htons(ETH_P_AX25);
-
-	dev_queue_xmit(skb);
-}
-
-void ax25_dama_on(ax25_cb *ax25)
-{
-	if (ax25_dev_is_dama_slave(ax25->device) == 0) {
-		SOCK_DEBUG(ax25->sk, "ax25_dama_on: DAMA on\n");
-		ax25_kiss_cmd(ax25, 5, 1);
-	}
-}
-
-void ax25_dama_off(ax25_cb *ax25)
-{
-	if (ax25->dama_slave == 0)
-		return;
-
-	ax25->dama_slave = 0;
-
-	if (ax25_dev_is_dama_slave(ax25->device) == 0) {
-		SOCK_DEBUG(ax25->sk, "ax25_dama_off: DAMA off\n");
-		ax25_kiss_cmd(ax25, 5, 0);
-	}
 }
 
 #endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v2.1.26/linux/net/ax25/ax25_timer.c	Sun Feb  2 05:18:48 1997
+++ linux/net/ax25/ax25_timer.c	Tue Feb 25 17:12:51 1997
@@ -1,5 +1,5 @@
 /*
- *	AX.25 release 035
+ *	AX.25 release 036
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
@@ -21,6 +21,7 @@
  *	AX.25 032	Joerg(DL1BKE)	Fixed DAMA timeout bug
  *	AX.25 033	Jonathan(G4KLX)	Modularisation functions.
  *	AX.25 035	Frederic(F1OAT)	Support for pseudo-digipeating.
+ *	AX.25 036	Jonathan(G4KLX)	Split Standard and DAMA code into seperate files.
  */
 
 #include <linux/config.h>
@@ -76,463 +77,16 @@
 {
 	ax25_cb *ax25 = (ax25_cb *)param;
 
-	switch (ax25->state) {
-		case AX25_STATE_0:
-			/* Magic here: If we listen() and a new link dies before it
-			   is accepted() it isn't 'dead' so doesn't get removed. */
-			if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
-				del_timer(&ax25->timer);
-				ax25_destroy_socket(ax25);
-				return;
-			}
+	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+		case AX25_PROTO_STD:
+			ax25_std_timer(ax25);
+			break;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+		case AX25_PROTO_DAMA_SLAVE:
+			ax25_ds_timer(ax25);
 			break;
-
-		case AX25_STATE_3:
-		case AX25_STATE_4:
-			/*
-			 * Check the state of the receive buffer.
-			 */
-			if (ax25->sk != NULL) {
-				if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
-					ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
-					ax25->condition &= ~AX25_COND_ACK_PENDING;
-					if (!ax25->dama_slave)
-						ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
-					break;
-				}
-			}
-			/*
-			 * Check for frames to transmit.
-			 */
-			if (!ax25->dama_slave)
-				ax25_kick(ax25);
-			break;
-
-		default:
-			break;
-	}
-
-	if (ax25->t2timer > 0 && --ax25->t2timer == 0) {
-		if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
-			if (ax25->condition & AX25_COND_ACK_PENDING) {
-				ax25->condition &= ~AX25_COND_ACK_PENDING;
-				if (!ax25->dama_slave)
-					ax25_timeout_response(ax25);
-			}
-		}
-	}
-
-	if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
-		/* dl1bke 960114: T3 expires and we are in DAMA mode:  */
-		/*                send a DISC and abort the connection */
-		if (ax25->dama_slave) {
-			ax25_link_failed(&ax25->dest_addr, ax25->device);
-			ax25_clear_queues(ax25);
-			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-
-			ax25->state = AX25_STATE_0;
-			if (ax25->sk != NULL) {
-				SOCK_DEBUG(ax25->sk, "AX.25 T3 Timeout\n");
-				ax25->sk->state     = TCP_CLOSE;
-				ax25->sk->err       = ETIMEDOUT;
-				ax25->sk->shutdown |= SEND_SHUTDOWN;
-				if (!ax25->sk->dead)
-					ax25->sk->state_change(ax25->sk);
-				ax25->sk->dead      = 1;
-			}
-
-			ax25_set_timer(ax25);
-			return;
-		}
-
-		if (ax25->state == AX25_STATE_3) {
-			ax25->n2count = 0;
-			ax25_transmit_enquiry(ax25);
-			ax25->state   = AX25_STATE_4;
-		}
-		ax25->t3timer = ax25->t3;
-	}
-
-	if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
-		/* dl1bke 960228: close the connection when IDLE expires */
-		/* 		  similar to DAMA T3 timeout but with    */
-		/* 		  a "clean" disconnect of the connection */
-
-		ax25_clear_queues(ax25);
-
-		ax25->n2count = 0;
-		if (!ax25->dama_slave) {
-			ax25->t3timer = 0;
-			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-		} else {
-			ax25->t3timer = ax25->t3;
-		}
-
-		/* state 1 or 2 should not happen, but... */
-
-		if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
-			ax25->state = AX25_STATE_0;
-		else
-			ax25->state = AX25_STATE_2;
-
-		ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-
-		if (ax25->sk != NULL) {
-			ax25->sk->state     = TCP_CLOSE;
-			ax25->sk->err       = 0;
-			ax25->sk->shutdown |= SEND_SHUTDOWN;
-			if (!ax25->sk->dead)
-				ax25->sk->state_change(ax25->sk);
-			ax25->sk->dead      = 1;
-			ax25->sk->destroy   = 1;
-		}
-	}
-
-	/* dl1bke 960114: DAMA T1 timeouts are handled in ax25_dama_slave_transmit */
-	/* 		  nevertheless we have to re-enqueue the timer struct...   */
-	if (ax25->t1timer == 0 || --ax25->t1timer > 0) {
-		ax25_set_timer(ax25);
-		return;
-	}
-
-	if (!ax25_dev_is_dama_slave(ax25->device)) {
-		if (ax25->dama_slave)
-			ax25->dama_slave = 0;
-		ax25_t1_timeout(ax25);
-	}
-}
-
-
-/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
- *                within the poll of any connected channel. Remember 
- *                that we are not allowed to send anything unless we
- *                get polled by the Master.
- *
- *                Thus we'll have to do parts of our T1 handling in
- *                ax25_enquiry_response().
- */
-void ax25_t1_timeout(ax25_cb *ax25)
-{	
-	switch (ax25->state) {
-		case AX25_STATE_1: 
-			if (ax25->n2count == ax25->n2) {
-				if (ax25->modulus == AX25_MODULUS) {
-					ax25_link_failed(&ax25->dest_addr, ax25->device);
-					ax25_clear_queues(ax25);
-					ax25->state = AX25_STATE_0;
-					if (ax25->sk != NULL) {
-						ax25->sk->state     = TCP_CLOSE;
-						ax25->sk->err       = ETIMEDOUT;
-						ax25->sk->shutdown |= SEND_SHUTDOWN;
-						if (!ax25->sk->dead)
-							ax25->sk->state_change(ax25->sk);
-						ax25->sk->dead      = 1;
-					}
-				} else {
-					ax25->modulus = AX25_MODULUS;
-					ax25->window  = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
-					ax25->n2count = 0;
-					ax25_send_control(ax25, AX25_SABM, ax25_dev_is_dama_slave(ax25->device) ? AX25_POLLOFF : AX25_POLLON, AX25_COMMAND);
-				}
-			} else {
-				ax25->n2count++;
-				if (ax25->modulus == AX25_MODULUS)
-					ax25_send_control(ax25, AX25_SABM, ax25_dev_is_dama_slave(ax25->device) ? AX25_POLLOFF : AX25_POLLON, AX25_COMMAND);
-				else
-					ax25_send_control(ax25, AX25_SABME, ax25_dev_is_dama_slave(ax25->device) ? AX25_POLLOFF : AX25_POLLON, AX25_COMMAND);
-			}
-			break;
-
-		case AX25_STATE_2:
-			if (ax25->n2count == ax25->n2) {
-				ax25_link_failed(&ax25->dest_addr, ax25->device);
-				ax25_clear_queues(ax25);
-				ax25->state = AX25_STATE_0;
-				ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-
-				if (ax25->sk != NULL) {
-					ax25->sk->state     = TCP_CLOSE;
-					ax25->sk->err       = ETIMEDOUT;
-					ax25->sk->shutdown |= SEND_SHUTDOWN;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead      = 1;
-				}
-			} else {
-				ax25->n2count++;
-				if (!ax25_dev_is_dama_slave(ax25->device))
-					ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-			}
-			break;
-
-		case AX25_STATE_3: 
-			ax25->n2count = 1;
-			if (!ax25->dama_slave)
-				ax25_transmit_enquiry(ax25);
-			ax25->state   = AX25_STATE_4;
-			break;
-
-		case AX25_STATE_4:
-			if (ax25->n2count == ax25->n2) {
-				ax25_link_failed(&ax25->dest_addr, ax25->device);
-				ax25_clear_queues(ax25);
-				ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
-				ax25->state = AX25_STATE_0;
-				if (ax25->sk != NULL) {
-					SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n");
-					ax25->sk->state     = TCP_CLOSE;
-					ax25->sk->err       = ETIMEDOUT;
-					ax25->sk->shutdown |= SEND_SHUTDOWN;
-					if (!ax25->sk->dead)
-						ax25->sk->state_change(ax25->sk);
-					ax25->sk->dead      = 1;
-				}
-			} else {
-				ax25->n2count++;
-				if (!ax25->dama_slave)
-					ax25_transmit_enquiry(ax25);
-			}
-			break;
-	}
-
-	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-
-	ax25_set_timer(ax25);
-}
-
-/************************************************************************/
-/*	Module support functions follow.				*/
-/************************************************************************/
-
-static struct protocol_struct {
-	struct protocol_struct *next;
-	unsigned int pid;
-	int (*func)(struct sk_buff *, ax25_cb *);
-} *protocol_list = NULL;
-
-static struct linkfail_struct {
-	struct linkfail_struct *next;
-	void (*func)(ax25_address *, struct device *);
-} *linkfail_list = NULL;
-
-static struct listen_struct {
-	struct listen_struct *next;
-	ax25_address  callsign;
-	struct device *dev;
-} *listen_list = NULL;
-
-int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
-{
-	struct protocol_struct *protocol;
-	unsigned long flags;
-
-	if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
-		return 0;
-#ifdef CONFIG_INET
-	if (pid == AX25_P_IP || pid == AX25_P_ARP)
-		return 0;
 #endif
-	if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
-		return 0;
-
-	protocol->pid  = pid;
-	protocol->func = func;
-
-	save_flags(flags);
-	cli();
-
-	protocol->next = protocol_list;
-	protocol_list  = protocol;
-
-	restore_flags(flags);
-
-	return 1;
-}
-
-void ax25_protocol_release(unsigned int pid)
-{
-	struct protocol_struct *s, *protocol = protocol_list;
-	unsigned long flags;
-
-	if (protocol == NULL)
-		return;
-
-	save_flags(flags);
-	cli();
-
-	if (protocol->pid == pid) {
-		protocol_list = protocol->next;
-		restore_flags(flags);
-		kfree_s(protocol, sizeof(struct protocol_struct));
-		return;
-	}
-
-	while (protocol != NULL && protocol->next != NULL) {
-		if (protocol->next->pid == pid) {
-			s = protocol->next;
-			protocol->next = protocol->next->next;
-			restore_flags(flags);
-			kfree_s(s, sizeof(struct protocol_struct));
-			return;
-		}
-
-		protocol = protocol->next;
 	}
-
-	restore_flags(flags);
-}
-
-int ax25_linkfail_register(void (*func)(ax25_address *, struct device *))
-{
-	struct linkfail_struct *linkfail;
-	unsigned long flags;
-
-	if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
-		return 0;
-
-	linkfail->func = func;
-
-	save_flags(flags);
-	cli();
-
-	linkfail->next = linkfail_list;
-	linkfail_list  = linkfail;
-
-	restore_flags(flags);
-
-	return 1;
-}
-
-void ax25_linkfail_release(void (*func)(ax25_address *, struct device *))
-{
-	struct linkfail_struct *s, *linkfail = linkfail_list;
-	unsigned long flags;
-
-	if (linkfail == NULL)
-		return;
-
-	save_flags(flags);
-	cli();
-
-	if (linkfail->func == func) {
-		linkfail_list = linkfail->next;
-		restore_flags(flags);
-		kfree_s(linkfail, sizeof(struct linkfail_struct));
-		return;
-	}
-
-	while (linkfail != NULL && linkfail->next != NULL) {
-		if (linkfail->next->func == func) {
-			s = linkfail->next;
-			linkfail->next = linkfail->next->next;
-			restore_flags(flags);
-			kfree_s(s, sizeof(struct linkfail_struct));
-			return;
-		}
-
-		linkfail = linkfail->next;
-	}
-
-	restore_flags(flags);
-}
-
-int ax25_listen_register(ax25_address *callsign, struct device *dev)
-{
-	struct listen_struct *listen;
-	unsigned long flags;
-
-	if (ax25_listen_mine(callsign, dev))
-		return 0;
-
-	if ((listen = (struct listen_struct *)kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
-		return 0;
-
-	listen->callsign = *callsign;
-	listen->dev      = dev;
-
-	save_flags(flags);
-	cli();
-
-	listen->next = listen_list;
-	listen_list  = listen;
-
-	restore_flags(flags);
-
-	return 1;
-}
-
-void ax25_listen_release(ax25_address *callsign, struct device *dev)
-{
-	struct listen_struct *s, *listen = listen_list;
-	unsigned long flags;
-
-	if (listen == NULL)
-		return;
-
-	save_flags(flags);
-	cli();
-
-	if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
-		listen_list = listen->next;
-		restore_flags(flags);
-		kfree_s(listen, sizeof(struct listen_struct));
-		return;
-	}
-
-	while (listen != NULL && listen->next != NULL) {
-		if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
-			s = listen->next;
-			listen->next = listen->next->next;
-			restore_flags(flags);
-			kfree_s(s, sizeof(struct listen_struct));
-			return;
-		}
-
-		listen = listen->next;
-	}
-
-	restore_flags(flags);
-}
-
-int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
-{
-	struct protocol_struct *protocol;
-
-	for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
-		if (protocol->pid == pid)
-			return protocol->func;
-
-	return NULL;
-}
-
-int ax25_listen_mine(ax25_address *callsign, struct device *dev)
-{
-	struct listen_struct *listen;
-
-	for (listen = listen_list; listen != NULL; listen = listen->next)
-		if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
-			return 1;
-
-	return 0;
-}
-
-void ax25_link_failed(ax25_address *callsign, struct device *dev)
-{
-	struct linkfail_struct *linkfail;
-
-	for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
-		(linkfail->func)(callsign, dev);
-}
-
-int ax25_protocol_is_registered(unsigned int pid)
-{
-	struct protocol_struct *protocol;
-
-	for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
-		if (protocol->pid == pid)
-			return 1;
-
-	return 0;
 }
 
 #endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/ax25_uid.c linux/net/ax25/ax25_uid.c
--- v2.1.26/linux/net/ax25/ax25_uid.c	Wed Dec 31 16:00:00 1969
+++ linux/net/ax25/ax25_uid.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,189 @@
+/*
+ *	AX.25 release 036
+ *
+ *	This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ *	releases, misbehave and/or generally screw up. It might even work.
+ *
+ *	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
+ *	AX.25 036	Jonathan(G4KLX)	Split from af_ax25.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/sysctl.h>
+#include <net/ip.h>
+#include <net/arp.h>
+
+/*
+ *	Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
+ */
+
+static ax25_uid_assoc *ax25_uid_list = NULL;
+
+int ax25_uid_policy = 0;
+
+ax25_address *ax25_findbyuid(uid_t uid)
+{
+	ax25_uid_assoc *ax25_uid;
+
+	for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
+		if (ax25_uid->uid == uid)
+			return &ax25_uid->call;
+	}
+
+	return NULL;
+}
+
+int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
+{
+	ax25_uid_assoc *s, *ax25_uid;
+	unsigned long flags;
+
+	switch (cmd) {
+		case SIOCAX25GETUID:
+			for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
+				if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
+					return ax25_uid->uid;
+			}
+			return -ENOENT;
+
+		case SIOCAX25ADDUID:
+			if (!suser())
+				return -EPERM;
+			if (ax25_findbyuid(sax->sax25_uid))
+				return -EEXIST;
+			if (sax->sax25_uid == 0)
+				return -EINVAL;
+			if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL)
+				return -ENOMEM;
+			ax25_uid->uid  = sax->sax25_uid;
+			ax25_uid->call = sax->sax25_call;
+			save_flags(flags); cli();
+			ax25_uid->next = ax25_uid_list;
+			ax25_uid_list  = ax25_uid;
+			restore_flags(flags);
+			return 0;
+
+		case SIOCAX25DELUID:
+			if (!suser())
+				return -EPERM;
+			for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
+				if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
+					break;
+			}
+			if (ax25_uid == NULL)
+				return -ENOENT;
+			save_flags(flags); cli();
+			if ((s = ax25_uid_list) == ax25_uid) {
+				ax25_uid_list = s->next;
+				restore_flags(flags);
+				kfree_s(ax25_uid, sizeof(ax25_uid_assoc));
+				return 0;
+			}
+			while (s != NULL && s->next != NULL) {
+				if (s->next == ax25_uid) {
+					s->next = ax25_uid->next;
+					restore_flags(flags);
+					kfree_s(ax25_uid, sizeof(ax25_uid_assoc));
+					return 0;
+				}
+				s = s->next;
+			}
+			restore_flags(flags);
+			return -ENOENT;
+
+		default:
+			return -EINVAL;
+	}
+
+	return -EINVAL;	/*NOTREACHED */
+}
+
+int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	ax25_uid_assoc *pt;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
+
+	for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
+		len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= offset - begin;
+
+	if (len > length) len = length;
+
+	return len;
+}
+
+#ifdef MODULE
+
+/*
+ *	Free all memory associated with UID/Callsign structures.
+ */
+void ax25_uid_free(void)
+{
+	ax25_uid_assoc *s, *ax25_uid = ax25_uid_list;
+
+	while (ax25_uid != NULL) {
+		s        = ax25_uid;
+		ax25_uid = ax25_uid->next;
+
+		kfree_s(s, sizeof(ax25_uid_assoc));
+	}
+}
+
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.26/linux/net/ax25/sysctl_net_ax25.c linux/net/ax25/sysctl_net_ax25.c
--- v2.1.26/linux/net/ax25/sysctl_net_ax25.c	Sun Feb  2 05:18:48 1997
+++ linux/net/ax25/sysctl_net_ax25.c	Tue Feb 25 17:12:51 1997
@@ -9,17 +9,27 @@
 #include <linux/sysctl.h>
 #include <net/ax25.h>
 
-static int min_ax25[] = {0, 0, 0, 0, 0, 1,  1,                1,                1,
-	                 0,                   0,  1,   1,  1, 0x00};
-static int max_ax25[] = {1, 1, 1, 2, 1, 7, 63, 30 * AX25_SLOWHZ, 20 * AX25_SLOWHZ,
-	3600 * AX25_SLOWHZ, 65535 * AX25_SLOWHZ, 31, 512, 20, 0x03};
+static int min_ipdefmode[] = {0},	max_ipdefmode[] = {1};
+static int min_axdefmode[] = {0},	max_axdefmode[] = {1};
+static int min_backoff[] = {0},		max_backoff[] = {2};
+static int min_conmode[] = {0},		max_conmode[] = {2};
+static int min_window[] = {1},		max_window[] = {7};
+static int min_ewindow[] = {1},		max_ewindow[] = {63};
+static int min_t1[] = {1},		max_t1[] = {30 * AX25_SLOWHZ};
+static int min_t2[] = {1},		max_t2[] = {20 * AX25_SLOWHZ};
+static int min_t3[] = {0},		max_t3[] = {3600 * AX25_SLOWHZ};
+static int min_idle[] = {0},		max_idle[] = {65535 * AX25_SLOWHZ};
+static int min_n2[] = {1},		max_n2[] = {31};
+static int min_paclen[] = {1},		max_paclen[] = {512};
+static int min_proto[] = {0},		max_proto[] = {2};
 
 static struct ctl_table_header *ax25_table_header;
 
-static ctl_table ax25_table[AX25_MAX_DEVICES + 1];
+static ctl_table *ax25_table = NULL;
+static int ax25_table_size   = 0;
 
 static ctl_table ax25_dir_table[] = {
-	{NET_AX25, "ax25", NULL, 0, 0555, ax25_table},
+	{NET_AX25, "ax25", NULL, 0, 0555, NULL},
 	{0}
 };
 
@@ -28,33 +38,104 @@
 	{0}
 };
 
+static const ctl_table ax25_param_table[] = {
+	{NET_AX25_IP_DEFAULT_MODE, "ip_default_mode",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_ipdefmode, &max_ipdefmode},
+	{NET_AX25_DEFAULT_MODE, "ax25_default_mode",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_axdefmode, &max_axdefmode},
+	{NET_AX25_BACKOFF_TYPE, "backoff_type",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_backoff, &max_backoff},
+	{NET_AX25_CONNECT_MODE, "connect_mode",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_conmode, &max_conmode},
+	{NET_AX25_STANDARD_WINDOW, "standard_window_size",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_window, &max_window},
+	{NET_AX25_EXTENDED_WINDOW, "extended_window_size",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_ewindow, &max_ewindow},
+	{NET_AX25_T1_TIMEOUT, "t1_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_t1, &max_t1},
+	{NET_AX25_T2_TIMEOUT, "t2_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_t2, &max_t2},
+	{NET_AX25_T3_TIMEOUT, "t3_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_t3, &max_t3},
+	{NET_AX25_IDLE_TIMEOUT, "idle_timeout",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_idle, &max_idle},
+	{NET_AX25_N2, "maximum_retry_count",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_n2, &max_n2},
+	{NET_AX25_PACLEN, "maximum_packet_length",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_paclen, &max_paclen},
+	{NET_AX25_PROTOCOL, "protocol",
+	 NULL, sizeof(int), 0644, NULL,
+	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
+	 &min_proto, &max_proto},
+	{0}	/* that's all, folks! */
+};
+
 void ax25_register_sysctl(void)
 {
-	int i, n;
+	ax25_dev *ax25_dev;
+	int n, k;
+
+	for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
+		ax25_table_size += sizeof(ctl_table);
+
+	if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL)
+		return;
+
+	memset(ax25_table, 0x00, ax25_table_size);
+
+	for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) {
+		ax25_table[n].ctl_name     = n + 1;
+		ax25_table[n].procname     = ax25_dev->dev->name;
+		ax25_table[n].data         = NULL;
+		ax25_table[n].maxlen       = 0;
+		ax25_table[n].mode         = 0555;
+		ax25_table[n].child        = ax25_dev->systable;
+		ax25_table[n].proc_handler = NULL;
 
-	memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table));
+		memcpy(ax25_dev->systable, ax25_param_table, sizeof(ax25_dev->systable));
 
-	for (n = 0, i = 0; i < AX25_MAX_DEVICES; i++) {
-		if (ax25_device[i].dev != NULL) {
-			ax25_table[n].ctl_name     = n + 1;
-			ax25_table[n].procname     = ax25_device[i].name;
-			ax25_table[n].data         = &ax25_device[i].values;
-			ax25_table[n].maxlen       = AX25_MAX_VALUES * sizeof(int);
-			ax25_table[n].mode         = 0644;
-			ax25_table[n].child        = NULL;
-			ax25_table[n].proc_handler = &proc_dointvec_minmax;
-			ax25_table[n].strategy     = &sysctl_intvec;
-			ax25_table[n].de           = NULL;
-			ax25_table[n].extra1       = &min_ax25;
-			ax25_table[n].extra2       = &max_ax25;
-			n++;
-		}
+		ax25_dev->systable[AX25_MAX_VALUES].ctl_name = 0;	/* just in case... */
+
+		for (k = 0; k < AX25_MAX_VALUES; k++)
+			ax25_dev->systable[k].data = &ax25_dev->values[k];
+
+		n++;
 	}
 
+	ax25_dir_table[0].child = ax25_table;
+
 	ax25_table_header = register_sysctl_table(ax25_root_table, 1);
 }
 
 void ax25_unregister_sysctl(void)
 {
 	unregister_sysctl_table(ax25_table_header);
+
+	kfree_s(ax25_table, ax25_table_size);
+
+	ax25_dir_table[0].child = NULL;
 }
diff -u --recursive --new-file v2.1.26/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.26/linux/net/core/sock.c	Tue Feb  4 06:44:25 1997
+++ linux/net/core/sock.c	Tue Feb 25 17:12:51 1997
@@ -145,7 +145,7 @@
 	}
 #endif	
 		
-  	if (optval == NULL) 
+  	if(optlen<sizeof(int))
   		return(-EINVAL);
   	
 	err = get_user(val, (int *)optval);
@@ -159,7 +159,7 @@
 		case SO_DEBUG:	
 			if(val && !suser())
 			{
-				ret = -EPERM;
+				ret = -EACCES;
 			}
 			else
 				sk->debug=valbool;
@@ -186,9 +186,10 @@
 				val = 65535;
 			sk->sndbuf = val;
 			/*
-			 *	FIXME: Wake up sending tasks if we
+			 *	Wake up sending tasks if we
 			 *	upped the value.
 			 */
+			sk->write_space(sk);
 			break;
 
 		case SO_RCVBUF:
@@ -221,17 +222,15 @@
 
 		case SO_PRIORITY:
 			if (val >= 0 && val < DEV_NUMBUFFS) 
-			{
 				sk->priority = val;
-			} 
 			else
-			{
 				return(-EINVAL);
-			}
 			break;
 
 
 		case SO_LINGER:
+			if(optlen<sizeof(ling))
+				return -EINVAL;	/* 1003.1g */
 			err = copy_from_user(&ling,optval,sizeof(ling));
 			if (err)
 			{
@@ -255,7 +254,7 @@
 			sock->passcred = valbool;
 			break;
 
-		/* We implementation the SO_SNDLOWAT etc to
+		/* We implement the SO_SNDLOWAT etc to
 		   not be settable (1003.1g 5.3) */
 		default:
 		  	return(-ENOPROTOOPT);
@@ -269,8 +268,11 @@
 {
 	struct sock *sk = sock->sk;
   	int val;
-  	int err;
   	struct linger ling;
+  	int len;
+  	
+  	if(get_user(len,optlen))
+  		return -EFAULT;
 
   	switch(optname) 
   	{
@@ -325,15 +327,19 @@
 			break;
 		
 		case SO_LINGER:	
-			err = put_user(sizeof(ling), optlen);
+		  {
+		        int err;
+			len=min(len,sizeof(ling));
+			if (put_user(len, optlen)) 
 			if (!err) {
 				ling.l_onoff=sk->linger;
 				ling.l_linger=sk->lingertime;
-				err = copy_to_user(optval,&ling,sizeof(ling));
+				err = copy_to_user(optval,&ling,len);
 				if (err)
 				    err = -EFAULT;
 			}
 			return err;
+		  }
 		
 		case SO_BSDCOMPAT:
 			val = sk->bsdism;
@@ -343,8 +349,10 @@
 		case SO_SNDTIMEO:
 		{
 			static struct timeval tm={0,0};
-			int err=copy_to_user(optval,&tm,sizeof(tm));
-			if(err!=sizeof(struct timeval))
+			len=min(len, sizeof(struct timeval));
+			if(put_user(len,optlen))
+				return -EFAULT;
+			if(copy_to_user(optval,&tm,len))
 				return -EFAULT;
 			return 0;
 		}
@@ -357,19 +365,22 @@
 			break;
 
 		case SO_PEERCRED:
-			err = put_user(sizeof(sk->peercred), optlen);
-			if (!err)
-				err = copy_to_user((void*)optval, &sk->peercred, sizeof(struct ucred));
-			return err;
-
+			len=min(len, sizeof(sk->peercred));
+			if(put_user(len, optlen))
+				return -EFAULT;
+			if(copy_to_user((void*)optval, &sk->peercred, len))
+				return -EFAULT;
+			return 0;
 		default:
 			return(-ENOPROTOOPT);
 	}
-  	err = put_user(sizeof(int), optlen);
-	if (!err)
-		err = put_user(val,(unsigned int *)optval);
+	len=min(len,sizeof(int));
+  	if(put_user(len, optlen))
+  		return -EFAULT;
+	if(copy_to_user(optval,&val,len))
+		return -EFAULT;
 
-  	return err;
+  	return 0;
 }
 
 static kmem_cache_t *sk_cachep;
@@ -743,6 +754,27 @@
 		default:
 			return(-EINVAL);
 	}
+}
+
+/*
+ *	Default socket getsockopt / setsockopt
+ */
+ 
+int sock_no_setsockopt(struct socket *sock, int level, int optname,
+		    char *optval, int optlen)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_getsockopt(struct socket *sock, int level, int optname,
+		    char *optval, int *optlen)
+{
+	return -EOPNOTSUPP;
+}
+
+int sock_no_listen(struct socket *sock, int backlog)
+{
+	return -EOPNOTSUPP;
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v2.1.26/linux/net/ethernet/eth.c	Sun Jan 26 02:07:50 1997
+++ linux/net/ethernet/eth.c	Fri Feb 21 14:58:38 1997
@@ -182,6 +182,7 @@
 		if (ndisc_eth_hook)
 			return (ndisc_eth_hook(eth->h_dest, dev, skb));
 #endif
+		break;
 #endif	
 	default:
 		printk(KERN_DEBUG 
diff -u --recursive --new-file v2.1.26/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.26/linux/net/ipv4/af_inet.c	Sun Feb  2 05:18:49 1997
+++ linux/net/ipv4/af_inet.c	Tue Feb 25 17:12:51 1997
@@ -1612,7 +1612,7 @@
 	inet_getname, 
 	datagram_poll,
 	inet_ioctl,
-	NULL,
+	sock_no_listen,
 	inet_shutdown,
 	inet_setsockopt,
 	inet_getsockopt,
diff -u --recursive --new-file v2.1.26/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.1.26/linux/net/ipv4/igmp.c	Thu Dec 12 06:54:23 1996
+++ linux/net/ipv4/igmp.c	Fri Feb 21 14:58:38 1997
@@ -64,6 +64,7 @@
  *		Alexey Kuznetsov:	Wrong group leaving behaviour, backport
  *					fix from pending 2.1.x patches.
  *		Alan Cox:		Forget to enable FDDI support earlier.
+ *		Alexey Kuznetsov:	Fixed leaving groups on device down.
  */
 
 
@@ -553,12 +554,16 @@
 {
 	struct ip_mc_list *i;
 	struct ip_mc_list *j;
+	start_bh_atomic();
 	for(i=dev->ip_mc_list;i!=NULL;i=j)
 	{
 		j=i->next;
+		if(i->tm_running)
+			del_timer(&i->timer);
 		kfree_s(i,sizeof(*i));
 	}
 	dev->ip_mc_list=NULL;
+	end_bh_atomic();
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.26/linux/net/ipv4/ip_fw.c	Sun Feb  2 05:18:49 1997
+++ linux/net/ipv4/ip_fw.c	Tue Feb 25 17:12:51 1997
@@ -571,7 +571,7 @@
 			answer = FW_BLOCK;
 
 #ifdef CONFIG_IP_FIREWALL_NETLINK
-		if(answer == FW_REJECT || answer == FW_BLOCK)
+		if((policy&IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK))
 		{
 			struct sk_buff *skb=alloc_skb(128, GFP_ATOMIC);
 			if(skb)
@@ -1320,4 +1320,8 @@
 	/* Register for device up/down reports */
 	register_netdevice_notifier(&ipfw_dev_notifier);
 #endif
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+   netlink_attach(NETLINK_FIREWALL, netlink_donothing); /* XXX */
+#endif /* CONFIG_IP_FIREWALL_NETLINK */
 }
diff -u --recursive --new-file v2.1.26/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.26/linux/net/ipv4/ip_output.c	Thu Dec 12 06:54:24 1996
+++ linux/net/ipv4/ip_output.c	Fri Feb 21 14:58:38 1997
@@ -972,12 +972,16 @@
 	if (dev->family != AF_INET)
 		return NOTIFY_DONE;
 
-	if(event==NETDEV_UP) {
-/*
- *	Join the initial group if multicast.
- */		
+	if(event==NETDEV_UP) 
+	{
+		/*
+		 *	Join the initial group if multicast.
+		 */		
 		ip_mc_allhost(dev);
 	}
+	if(event==NETDEV_DOWN)
+		ip_mc_drop_device(dev);
+		
 	return ip_rt_event(event, dev);
 }
 
diff -u --recursive --new-file v2.1.26/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.26/linux/net/ipv4/ip_sockglue.c	Sun Jan 26 02:07:50 1997
+++ linux/net/ipv4/ip_sockglue.c	Tue Feb 25 17:12:51 1997
@@ -169,27 +169,22 @@
 
 int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
 {
-	int val,err;
+	int val=0,err;
 	unsigned char ucval = 0;
+	int len;
 #if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
 	struct ip_fw tmp_fw;
 #endif	
-	if (optval == NULL)
-	{
-		val=0;
-		ucval=0;
-	}
-	else
-	{
-		err = get_user(val, (int *) optval);
-		if (!err)
-			err = get_user(ucval, (unsigned char *) optval);
-		if (err)
-			return err; 
-	}
+
+	if(get_user(len, optval))
+		return -EFAULT;
+	if(len>=sizeof(int) && get_user(val, (int *) optval))
+		return -EFAULT;
+	if(len>=sizeof(char) && get_user(ucval, (unsigned char *) optval))
+		return -EFAULT;
 	
 	if(level!=SOL_IP)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
 #ifdef CONFIG_IP_MROUTE
 	if(optname>=MRT_BASE && optname <=MRT_BASE+10)
 	{
@@ -200,51 +195,61 @@
 	switch(optname)
 	{
 		case IP_OPTIONS:
-	          {
-			  struct ip_options * opt = NULL;
-			  struct ip_options * old_opt;
-			  if (optlen > 40 || optlen < 0)
-			  	return -EINVAL;
-			  err = ip_options_get(&opt, optval, optlen, 1);
-			  if (err)
-			  	return err;
-			  /*
-			   * ANK: I'm afraid that receive handler may change
-			   * options from under us.
-			   */
-			  cli();
-			  old_opt = sk->opt;
-			  sk->opt = opt;
-			  sti();
-			  if (old_opt)
-			  	kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
-			  return 0;
-		  }
+		{
+			struct ip_options * opt = NULL;
+			struct ip_options * old_opt;
+			if (optlen > 40 || optlen < 0)
+				return -EINVAL;
+			err = ip_options_get(&opt, optval, optlen, 1);
+			if (err)
+				return err;
+			/*
+			 * ANK: I'm afraid that receive handler may change
+			 * options from under us.
+			 */
+			cli();
+			old_opt = sk->opt;
+			sk->opt = opt;
+			sti();
+			if (old_opt)
+				kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
+			return 0;
+		}
 		case IP_RXINFO:
+			if (optlen<4)
+				return -EINVAL;
 			if (val)
 				sk->ip_cmsg_flags |= 1;
 			else
 				sk->ip_cmsg_flags &= ~1;
 			return 0;
 		case IP_LOCALADDR:
+			if (optlen<4)
+				return -EINVAL;
 			if (val)
 				sk->ip_cmsg_flags |= 2;
 			else
 				sk->ip_cmsg_flags &= ~2;
 			return 0;
 		case IP_RECVOPTS:
+			if (optlen<4)
+				return -EINVAL;
 			if (val)
 				sk->ip_cmsg_flags |= 4;
 			else
 				sk->ip_cmsg_flags &= ~4;
 			return 0;
 		case IP_RETOPTS:
+			if (optlen<4)
+				return -EINVAL;
 			if (val)
 				sk->ip_cmsg_flags |= 8;
 			else
 				sk->ip_cmsg_flags &= ~8;
 			return 0;
 		case IP_RECVDSTADDR:
+			if (optlen<4)
+				return -EINVAL;
 			if (val)
 				sk->ip_cmsg_flags |= 0x10;
 			else
@@ -252,6 +257,8 @@
 			return 0;
 		case IP_TOS:	/* This sets both TOS and Precedence */
 			  /* Reject setting of unused bits */
+			if (optlen<4)
+				return -EINVAL;
 			if (val & ~(IPTOS_TOS_MASK|IPTOS_PREC_MASK))
 				return -EINVAL;
 			if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !suser())
@@ -260,21 +267,29 @@
 			sk->priority = rt_tos2priority(val);
 			return 0;
 		case IP_TTL:
+			if (optlen<4)
+				return -EINVAL;
 			if(val<1||val>255)
 				return -EINVAL;
 			sk->ip_ttl=val;
 			return 0;
 		case IP_HDRINCL:
+			if (optlen<4)
+				return -EINVAL;
 			if(sk->type!=SOCK_RAW)
 				return -ENOPROTOOPT;
 			sk->ip_hdrincl=val?1:0;
 			return 0;
 		case IP_PMTUDISC:
+			if (optlen<4)
+				return -EINVAL;
 			if (val<0 || val>2)
 				return -EINVAL;
 			sk->ip_pmtudisc = val;
 			return 0;
 		case IP_RECVERR:
+			if (optlen<4)
+				return -EINVAL;
 			if (sk->type==SOCK_STREAM)
 				return -ENOPROTOOPT;
 			lock_sock(sk);
@@ -290,9 +305,13 @@
 			release_sock(sk);
 			return 0;
 		case IP_MULTICAST_TTL: 
+			if (optlen<1)
+				return -EINVAL;
 			sk->ip_mc_ttl=(int)ucval;
 	                return 0;
 		case IP_MULTICAST_LOOP: 
+			if (optlen<1)
+				return -EINVAL;
 			if(ucval!=0 && ucval!=1)
 				 return -EINVAL;
 			sk->ip_mc_loop=(int)ucval;
@@ -306,8 +325,10 @@
 			 *	Check the arguments are allowable
 			 */
 
-			err = copy_from_user(&addr,optval,sizeof(addr));
-			if (err)
+			if(optlen<sizeof(addr))
+				return -EINVAL;
+				
+			if(copy_from_user(&addr,optval,sizeof(addr)))
 				return -EFAULT; 
 
 			
@@ -354,9 +375,10 @@
 			/*
 			 *	Check the arguments.
 			 */
-
-			err = copy_from_user(&mreq,optval,sizeof(mreq));
-			if (err)
+			 
+			if(optlen<sizeof(mreq))
+				return -EINVAL;
+			if(copy_from_user(&mreq,optval,sizeof(mreq)))
 				return -EFAULT; 
 
 			/* 
@@ -396,8 +418,9 @@
 			 *	Check the arguments
 			 */
 			 
-			err = copy_from_user(&mreq,optval,sizeof(mreq));
-			if (err)
+			if(optlen<sizeof(mreq))
+				return -EINVAL;
+			if(copy_from_user(&mreq,optval,sizeof(mreq)))
 				return -EFAULT; 
 
 			/*
@@ -432,8 +455,9 @@
 			struct ip_mreqn mreq;
 			struct device *dev = NULL;
 			
-			err = copy_from_user(&mreq,optval,sizeof(mreq));
-			if (err)
+			if(optlen<sizeof(mreq))
+				return -EINVAL;
+			if(copy_from_user(&mreq,optval,sizeof(mreq)))
 				return -EFAULT;
 
 			if (!mreq.imr_ifindex) {
@@ -457,9 +481,10 @@
 		{
 			struct ip_mreqn mreq;
 			struct device *dev = NULL;
-			
-			err = copy_from_user(&mreq,optval,sizeof(mreq));
-			if (err)
+
+			if(optlen<sizeof(mreq))
+				return -EINVAL;			
+			if(copy_from_user(&mreq,optval,sizeof(mreq)))
 				return -EFAULT; 
 			dev = dev_get_by_index(mreq.imr_ifindex);
 			if (!dev)
@@ -475,9 +500,10 @@
 			/*
 			 *	Check the arguments
 			 */
-			 
-			err = copy_from_user(&mreq,optval,sizeof(mreq));
-			if (err)
+
+			if(optlen<sizeof(mreq))
+				return -EINVAL;			 
+			if(copy_from_user(&mreq,optval,sizeof(mreq)))
 				return -EFAULT; 
 
 			dev=dev_get_by_index(mreq.imr_ifindex);
@@ -510,12 +536,11 @@
 		case IP_FW_POLICY_FWD:
 		case IP_FW_MASQ_TIMEOUTS:
 			if(!suser())
-				return -EPERM;
+				return -EACCES;
 			if(optlen>sizeof(tmp_fw) || optlen<1)
 				return -EINVAL;
-			err = copy_from_user(&tmp_fw,optval,optlen);
-			if (err)
-				return -EFAULT; 
+			if(copy_from_user(&tmp_fw,optval,optlen))
+				return -EFAULT;
 			err=ip_fw_ctl(optname, &tmp_fw,optlen);
 			return -err;	/* -0 is 0 after all */
 			
@@ -527,11 +552,10 @@
 		case IP_ACCT_FLUSH:
 		case IP_ACCT_ZERO:
 			if(!suser())
-				return -EPERM;
+				return -EACCES;
 			if(optlen>sizeof(tmp_fw) || optlen<1)
 				return -EINVAL;
-			err = copy_from_user(&tmp_fw, optval,optlen);
-			if (err)
+			if(copy_from_user(&tmp_fw, optval,optlen))
 				return -EFAULT; 
 			err=ip_acct_ctl(optname, &tmp_fw,optlen);
 			return -err;	/* -0 is 0 after all */
@@ -561,6 +585,9 @@
 	}
 #endif
 
+	if(get_user(len,optlen))
+		return -EFAULT;
+
 	switch(optname)
 	{
 		case IP_OPTIONS:
@@ -577,15 +604,13 @@
 
 				ip_options_undo(opt);
 
-				err = put_user(opt->optlen, optlen);
-				if (!err) 
-				{
-					if(copy_to_user(optval, opt->__data, opt->optlen))
-					    err = -EFAULT;
-				}
-				return err; 
+				len=min(len, opt->optlen);
+				if(put_user(len, optlen))
+					return -EFAULT;
+				if(copy_to_user(optval, opt->__data, len))
+					    return -EFAULT;
+				return 0;
 			}
-			return 0;
 		case IP_RXINFO:
 			val = (sk->ip_cmsg_flags & 1) != 0;
 			return 0;
@@ -625,33 +650,32 @@
 		case IP_MULTICAST_IFN:
 		{
 			struct ip_mreqn mreq;
-			len = sizeof(struct ip_mreqn);
-  			err = put_user(len, optlen);
+			len = min(len,sizeof(struct ip_mreqn));
+  			if(put_user(len, optlen))
+  				return -EFAULT;
 			mreq.imr_ifindex = sk->ip_mc_index;
 			mreq.imr_address.s_addr = sk->ip_mc_addr;
 			mreq.imr_multiaddr.s_addr = 0;
-			if (!err) {
-				err = copy_to_user((void *)optval, &mreq, len);
-				if (err)
-					err = -EFAULT;
-			}
-			return err;
+			if(copy_to_user((void *)optval, &mreq, len))
+				return -EFAULT;
+			return 0;
 		}
 		case IP_MULTICAST_IF:
 		{
 			struct device *dev = dev_get_by_index(sk->ip_mc_index);
-			if (dev == NULL) {
+			if (dev == NULL) 
+			{
 				len = 0;
 				return put_user(len, optlen);
 			}
 			dev_lock_list();
-  			len = strlen(dev->name);
+  			len = min(len,strlen(dev->name));
   			err = put_user(len, optlen);
 			if (!err)
 			{
 				err = copy_to_user((void *)optval,dev->name, len);
-				if (err)
-				    err = -EFAULT;
+				if(err)
+					err=-EFAULT;
 			}
 			dev_unlock_list();
 			return err;
@@ -659,10 +683,12 @@
 		default:
 			return(-ENOPROTOOPT);
 	}
-	err = put_user(sizeof(int), optlen);
-	if (err) 
-		return err; 
-	return put_user(val,(int *) optval);
-
-	return(0);
+	
+	len=min(sizeof(int),len);
+	
+	if(put_user(len, optlen))
+		return -EFAULT;
+	if(copy_to_user(optval,&val,len))
+		return -EFAULT;
+	return 0;
 }
diff -u --recursive --new-file v2.1.26/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.1.26/linux/net/ipv4/ipmr.c	Thu Dec 12 06:54:24 1996
+++ linux/net/ipv4/ipmr.c	Fri Feb 21 14:58:38 1997
@@ -550,6 +550,8 @@
 		 */
 		case MRT_ADD_MFC:
 		case MRT_DEL_MFC:
+			if(optlen!=sizeof(mfc))
+				return -EINVAL;
 			err = copy_from_user(&mfc,optval, sizeof(mfc));
 			return err ? -EFAULT : ipmr_mfc_modify(optname, &mfc);
 		/*
@@ -558,9 +560,6 @@
 		case MRT_ASSERT:
 		{
 			int v;
-			if(optlen!=sizeof(int))
-				return -EINVAL;
-			
 			if(get_user(v,(int *)optval))
 				return -EFAULT;
 			mroute_do_pim=(v)?1:0;
@@ -571,7 +570,7 @@
 		 *	set.
 		 */
 		default:
-			return -EOPNOTSUPP;
+			return -ENOPROTOOPT;
 	}
 }
 
@@ -582,26 +581,26 @@
 int ip_mroute_getsockopt(struct sock *sk,int optname,char *optval,int *optlen)
 {
 	int olr;
-	int err;
+	int val;
 
 	if(sk!=mroute_socket)
 		return -EACCES;
 	if(optname!=MRT_VERSION && optname!=MRT_ASSERT)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
 	
-	err = get_user(olr, optlen);
-	if (err)
-		return err; 
-	if(olr!=sizeof(int))
-		return -EINVAL;
-	err = put_user(sizeof(int),optlen);
-	if (err)
-		return err; 
+	if(get_user(olr, optlen))
+		return -EFAULT;
+
+	olr=min(olr,sizeof(int));
+	if(put_user(olr,optlen))
+		return -EFAULT;
 	if(optname==MRT_VERSION)
-		err = put_user(0x0305,(int *)optval);
+		val=0x0305;
 	else
-		err = put_user(mroute_do_pim,(int *)optval);
-	return err;
+		val=mroute_do_pim;
+	if(copy_to_user(optval,&val,olr))
+		return -EFAULT;
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.26/linux/net/ipv4/tcp.c	Fri Feb  7 05:54:55 1997
+++ linux/net/ipv4/tcp.c	Fri Feb 21 14:58:38 1997
@@ -1777,13 +1777,11 @@
 	int val;
 
 	if (level != SOL_TCP)
-	{
 		return tp->af_specific->setsockopt(sk, level, optname, 
 						   optval, optlen);
-	}
-
-  	if (optval == NULL)
-  		return(-EINVAL);
+	
+	if(optlen<sizeof(int))
+		return -EINVAL;
 
   	if (get_user(val, (int *)optval))
 		return -EFAULT;
@@ -1812,13 +1810,19 @@
 		   int *optlen)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	int val,err;
+	int val;
+	int len;
 
 	if(level != SOL_TCP)
 	{
 		return tp->af_specific->getsockopt(sk, level, optname,
 						   optval, optlen);
 	}
+	
+	if(get_user(len,optlen))
+		return -EFAULT;
+		
+	len=min(len,sizeof(int));
 
 	switch(optname)
 	{
@@ -1832,11 +1836,11 @@
 			return(-ENOPROTOOPT);
 	}
 
-  	err = put_user(sizeof(int),(int *) optlen);
-	if (!err)
-		err = put_user(val,(int *)optval);
-
-  	return err;
+  	if(put_user(len, optlen))
+  		return -EFAULT;
+	if(copy_to_user(optval, &val,len))
+		return -EFAULT;
+  	return 0;
 }
 
 void tcp_set_keepalive(struct sock *sk, int val)
diff -u --recursive --new-file v2.1.26/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.26/linux/net/ipv6/addrconf.c	Sun Jan 19 05:47:28 1997
+++ linux/net/ipv6/addrconf.c	Fri Feb 21 14:58:39 1997
@@ -27,6 +27,7 @@
 #include <linux/in6.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
+#include <linux/route.h>
 
 #include <linux/proc_fs.h>
 #include <net/sock.h>
diff -u --recursive --new-file v2.1.26/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.26/linux/net/ipv6/af_inet6.c	Sun Feb  2 05:18:51 1997
+++ linux/net/ipv6/af_inet6.c	Tue Feb 25 17:12:51 1997
@@ -751,7 +751,7 @@
 	inet6_getname, 
 	datagram_poll,			/* ok		*/
 	inet6_ioctl,			/* must change  */
-	inet_listen,			/* ok		*/
+	sock_no_listen,			/* ok		*/
 	inet_shutdown,			/* ok		*/
 	inet_setsockopt,		/* ok		*/
 	inet_getsockopt,		/* ok		*/
diff -u --recursive --new-file v2.1.26/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- v2.1.26/linux/net/ipv6/datagram.c	Sun Jan 19 05:47:28 1997
+++ linux/net/ipv6/datagram.c	Fri Feb 21 14:58:40 1997
@@ -19,6 +19,7 @@
 #include <linux/sockios.h>
 #include <linux/in6.h>
 #include <linux/ipv6.h>
+#include <linux/route.h>
 
 #include <net/ipv6.h>
 #include <net/ndisc.h>
diff -u --recursive --new-file v2.1.26/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.26/linux/net/ipv6/ipv6_sockglue.c	Mon Jan 13 22:16:49 1997
+++ linux/net/ipv6/ipv6_sockglue.c	Fri Feb 21 14:58:40 1997
@@ -13,6 +13,12 @@
  *      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.
+ *
+ *	FIXME: Make the setsockopt code POSIX compliant: That is
+ *
+ *	o	Return -EINVAL for setsockopt of short lengths
+ *	o	Truncate getsockopt returns
+ *	o	Return an optlen of the truncated length if need be
  */
 
 #include <linux/errno.h>
@@ -67,7 +73,7 @@
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	int val, err;
-	int retv = -EOPNOTSUPP;
+	int retv = -ENOPROTOOPT;
 
 	if(level!=SOL_IPV6)
 		goto out;
diff -u --recursive --new-file v2.1.26/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c
--- v2.1.26/linux/net/ipv6/mcast.c	Sun Nov  3 01:04:46 1996
+++ linux/net/ipv6/mcast.c	Fri Feb 21 14:58:40 1997
@@ -22,6 +22,7 @@
 #include <linux/in6.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
+#include <linux/route.h>
 
 #include <net/sock.h>
 #include <net/snmp.h>
diff -u --recursive --new-file v2.1.26/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.26/linux/net/ipv6/ndisc.c	Thu Jan  2 04:07:39 1997
+++ linux/net/ipv6/ndisc.c	Fri Feb 21 14:58:40 1997
@@ -35,6 +35,7 @@
 #include <linux/sched.h>
 #include <linux/net.h>
 #include <linux/in6.h>
+#include <linux/route.h>
 
 #include <net/neighbour.h>
 #include <linux/if_arp.h>
@@ -1847,6 +1848,9 @@
 #else
 	proc_net_register(&ndisc_proc_entry);
 #endif
+#endif
+#ifdef CONFIG_IPV6_MODULE
+	ndisc_eth_hook = ndisc_eth_resolv;
 #endif
 }
 
diff -u --recursive --new-file v2.1.26/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.26/linux/net/ipx/af_ipx.c	Fri Feb  7 05:54:55 1997
+++ linux/net/ipx/af_ipx.c	Tue Feb 25 17:12:51 1997
@@ -1721,7 +1721,7 @@
 
 	sk=sock->sk;
 
-	if (optval==NULL)
+	if (optlen!=sizeof(int))
 		return(-EINVAL);
 
 	err = get_user(opt, (unsigned int *)optval);
@@ -1737,12 +1737,12 @@
 					sk->protinfo.af_ipx.type=opt;
 					return 0;
 				default:
-					return -EOPNOTSUPP;
+					return -ENOPROTOOPT;
 			}
 			break;
 
 		default:
-			return -EOPNOTSUPP;
+			return -ENOPROTOOPT;
 	}
 }
 
@@ -1751,7 +1751,7 @@
 {
 	struct sock *sk;
 	int val=0;
-	int err;
+	int len;
 
 	sk=sock->sk;
 
@@ -1770,17 +1770,16 @@
 			break;
 
 		default:
-			return -EOPNOTSUPP;
+			return -ENOPROTOOPT;
 	}
-	err = put_user(sizeof(int), optlen);
-	if (!err)
-		err = put_user(val, (int *)optval);
-	return err;
-}
-
-static int ipx_listen(struct socket *sock, int backlog)
-{
-	return -EOPNOTSUPP;
+	if(get_user(len,optlen))
+		return -EFAULT;
+	len=min(len,sizeof(int));
+	if(put_user(len, optlen))
+		return -EFAULT;
+	if(copy_to_user(optval,&val,len))
+		return -EFAULT;
+	return 0;
 }
 
 static int ipx_create(struct socket *sock, int protocol)
@@ -2334,7 +2333,7 @@
 	ipx_getname,
 	datagram_poll,
 	ipx_ioctl,
-	ipx_listen,
+	sock_no_listen,
 	ipx_shutdown,
 	ipx_setsockopt,
 	ipx_getsockopt,
diff -u --recursive --new-file v2.1.26/linux/net/lapb/lapb_iface.c linux/net/lapb/lapb_iface.c
--- v2.1.26/linux/net/lapb/lapb_iface.c	Tue Feb  4 06:44:25 1997
+++ linux/net/lapb/lapb_iface.c	Fri Feb 21 14:58:40 1997
@@ -14,9 +14,6 @@
  *
  *	History
  *	LAPB 001	Jonathan Naylor	Started Coding
- *
- *	TODO
- *	Add FRMRs.
  */
  
 #include <linux/config.h>
diff -u --recursive --new-file v2.1.26/linux/net/lapb/lapb_in.c linux/net/lapb/lapb_in.c
--- v2.1.26/linux/net/lapb/lapb_in.c	Tue Feb  4 06:44:25 1997
+++ linux/net/lapb/lapb_in.c	Fri Feb 21 14:58:40 1997
@@ -43,26 +43,26 @@
  *	State machine for state 0, Disconnected State.
  *	The handling of the timer(s) is in file lapb_timer.c.
  */
-static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf)
+static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
 {
-	switch (frametype) {
+	switch (frame->type) {
 		case LAPB_SABM:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S0 RX SABM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S0 RX SABM(%d)\n", lapb->token, frame->pf);
 #endif
 			if (lapb->mode & LAPB_EXTENDED) {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			} else {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
 #if LAPB_DEBUG > 0
 				printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
 #endif
-				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 				lapb->state     = LAPB_STATE_3;
 				lapb->condition = 0x00;
 				lapb->t1timer   = 0;
@@ -77,16 +77,16 @@
 
 		case LAPB_SABME:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S0 RX SABME(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S0 RX SABME(%d)\n", lapb->token, frame->pf);
 #endif
 			if (lapb->mode & LAPB_EXTENDED) {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
 #if LAPB_DEBUG > 0
 				printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
 #endif
-				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 				lapb->state     = LAPB_STATE_3;
 				lapb->condition = 0x00;
 				lapb->t1timer   = 0;
@@ -98,18 +98,18 @@
 				lapb_connect_indication(lapb, LAPB_OK);
 			} else {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			}
 			break;
 
 		case LAPB_DISC:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S0 RX DISC(%d)\n", lapb->token, pf);
-			printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S0 RX DISC(%d)\n", lapb->token, frame->pf);
+			printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
-			lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 			break;
 
 		default:
@@ -123,56 +123,56 @@
  *	State machine for state 1, Awaiting Connection State.
  *	The handling of the timer(s) is in file lapb_timer.c.
  */
-static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf)
+static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
 {
-	switch (frametype) {
+	switch (frame->type) {
 		case LAPB_SABM:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S1 RX SABM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S1 RX SABM(%d)\n", lapb->token, frame->pf);
 #endif
 			if (lapb->mode & LAPB_EXTENDED) {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			} else {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 			}
 			break;
 
 		case LAPB_SABME:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S1 RX SABME(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S1 RX SABME(%d)\n", lapb->token, frame->pf);
 #endif
 			if (lapb->mode & LAPB_EXTENDED) {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 			} else {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			}
 			break;
 
 		case LAPB_DISC:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S1 RX DISC(%d)\n", lapb->token, pf);
-			printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S1 RX DISC(%d)\n", lapb->token, frame->pf);
+			printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-			lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			break;
 
 		case LAPB_UA:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S1 RX UA(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S1 RX UA(%d)\n", lapb->token, frame->pf);
 #endif
-			if (pf) {
+			if (frame->pf) {
 #if LAPB_DEBUG > 0
 				printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->token);
 #endif
@@ -190,9 +190,9 @@
 
 		case LAPB_DM:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S1 RX DM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S1 RX DM(%d)\n", lapb->token, frame->pf);
 #endif
-			if (pf) {
+			if (frame->pf) {
 #if LAPB_DEBUG > 0
 				printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
 #endif
@@ -215,31 +215,31 @@
  *	State machine for state 2, Awaiting Release State.
  *	The handling of the timer(s) is in file lapb_timer.c
  */
-static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf)
+static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
 {
-	switch (frametype) {
+	switch (frame->type) {
 		case LAPB_SABM:
 		case LAPB_SABME:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S2 RX {SABM,SABME}(%d)\n", lapb->token, pf);
-			printk(KERN_DEBUG "lapb: (%p) S2 TX DM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 RX {SABM,SABME}(%d)\n", lapb->token, frame->pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-			lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			break;
 
 		case LAPB_DISC:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S2 RX DISC(%d)\n", lapb->token, pf);
-			printk(KERN_DEBUG "lapb: (%p) S2 TX UA(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 RX DISC(%d)\n", lapb->token, frame->pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
-			lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 			break;
 
 		case LAPB_UA:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S2 RX UA(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 RX UA(%d)\n", lapb->token, frame->pf);
 #endif
-			if (pf) {
+			if (frame->pf) {
 #if LAPB_DEBUG > 0
 				printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
 #endif
@@ -252,9 +252,9 @@
 
 		case LAPB_DM:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, frame->pf);
 #endif
-			if (pf) {
+			if (frame->pf) {
 #if LAPB_DEBUG > 0
 				printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
 #endif
@@ -270,10 +270,10 @@
 		case LAPB_RNR:
 		case LAPB_RR:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S2 RX {I,REJ,RNR,RR}(%d)\n", lapb->token, pf);
-			printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 RX {I,REJ,RNR,RR}(%d)\n", lapb->token, frame->pf);
+			printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, frame->pf);
 #endif
-			if (pf) lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+			if (frame->pf) lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			break;
 				
 		default:
@@ -287,28 +287,28 @@
  *	State machine for state 3, Connected State.
  *	The handling of the timer(s) is in file lapb_timer.c
  */
-static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
 {
 	int queued = 0;
 	int modulus;
 	
 	modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
 
-	switch (frametype) {
+	switch (frame->type) {
 		case LAPB_SABM:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX SABM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX SABM(%d)\n", lapb->token, frame->pf);
 #endif
 			if (lapb->mode & LAPB_EXTENDED) {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			} else {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 				lapb->condition = 0x00;
 				lapb->t1timer   = 0;
 				lapb->t2timer   = 0;
@@ -322,13 +322,13 @@
 
 		case LAPB_SABME:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX SABME(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX SABME(%d)\n", lapb->token, frame->pf);
 #endif
 			if (lapb->mode & LAPB_EXTENDED) {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 				lapb->condition = 0x00;
 				lapb->t1timer   = 0;
 				lapb->t2timer   = 0;
@@ -339,21 +339,21 @@
 				lapb_requeue_frames(lapb);
 			} else {
 #if LAPB_DEBUG > 1
-				printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, pf);
+				printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, frame->pf);
 #endif
-				lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE);
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
 			}
 			break;
 
 		case LAPB_DISC:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX DISC(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX DISC(%d)\n", lapb->token, frame->pf);
 #endif
 #if LAPB_DEBUG > 0
 			printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
 #endif
 			lapb_clear_queues(lapb);
-			lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE);
+			lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
 			lapb->state   = LAPB_STATE_0;
 			lapb->t1timer = lapb->t1;
 			lapb->t2timer = 0;
@@ -362,7 +362,7 @@
 
 		case LAPB_DM:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX DM(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX DM(%d)\n", lapb->token, frame->pf);
 #endif
 #if LAPB_DEBUG > 0
 			printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
@@ -376,80 +376,100 @@
 
 		case LAPB_RNR:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX RNR(%d) R%d\n", lapb->token, pf, nr);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX RNR(%d) R%d\n", lapb->token, frame->pf, frame->nr);
 #endif
 			lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION;
-			lapb_check_need_response(lapb, type, pf);
-			if (lapb_validate_nr(lapb, nr)) {
-				lapb_check_iframes_acked(lapb, nr);
-			} else {
+			lapb_check_need_response(lapb, frame->cr, frame->pf);
+			if (lapb_validate_nr(lapb, frame->nr)) {
+				lapb_check_iframes_acked(lapb, frame->nr);
+			} else {
+				lapb->frmr_data = *frame;
+				lapb->frmr_type = LAPB_FRMR_Z;
+				lapb_transmit_frmr(lapb);
 #if LAPB_DEBUG > 0
-				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
 #endif
-				lapb_establish_data_link(lapb);
-				lapb->state = LAPB_STATE_1;
+				lapb->state     = LAPB_STATE_4;
+				lapb->t1timer   = lapb->t1;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
 			}
 			break;
 
 		case LAPB_RR:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX RR(%d) R%d\n", lapb->token, pf, nr);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX RR(%d) R%d\n", lapb->token, frame->pf, frame->nr);
 #endif
 			lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
-			lapb_check_need_response(lapb, type, pf);
-			if (lapb_validate_nr(lapb, nr)) {
-				lapb_check_iframes_acked(lapb, nr);
-			} else {
+			lapb_check_need_response(lapb, frame->cr, frame->pf);
+			if (lapb_validate_nr(lapb, frame->nr)) {
+				lapb_check_iframes_acked(lapb, frame->nr);
+			} else {
+				lapb->frmr_data = *frame;
+				lapb->frmr_type = LAPB_FRMR_Z;
+				lapb_transmit_frmr(lapb);
 #if LAPB_DEBUG > 0
-				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
 #endif
-				lapb_establish_data_link(lapb);
-				lapb->state = LAPB_STATE_1;
+				lapb->state     = LAPB_STATE_4;
+				lapb->t1timer   = lapb->t1;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
 			}
 			break;
 
 		case LAPB_REJ:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX REJ(%d) R%d\n", lapb->token, pf, nr);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX REJ(%d) R%d\n", lapb->token, frame->pf, frame->nr);
 #endif
 			lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
-			lapb_check_need_response(lapb, type, pf);
-			if (lapb_validate_nr(lapb, nr)) {
-				lapb_frames_acked(lapb, nr);
+			lapb_check_need_response(lapb, frame->cr, frame->pf);
+			if (lapb_validate_nr(lapb, frame->nr)) {
+				lapb_frames_acked(lapb, frame->nr);
 				lapb->t1timer = 0;
 				lapb->n2count = 0;
 				lapb_requeue_frames(lapb);
 			} else {
+				lapb->frmr_data = *frame;
+				lapb->frmr_type = LAPB_FRMR_Z;
+				lapb_transmit_frmr(lapb);
 #if LAPB_DEBUG > 0
-				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
 #endif
-				lapb_establish_data_link(lapb);
-				lapb->state = LAPB_STATE_1;
+				lapb->state     = LAPB_STATE_4;
+				lapb->t1timer   = lapb->t1;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
 			}
 			break;
 
 		case LAPB_I:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX I(%d) S%d R%d\n", lapb->token, pf, ns, nr);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX I(%d) S%d R%d\n", lapb->token, frame->pf, frame->ns, frame->nr);
 #endif
-			if (!lapb_validate_nr(lapb, nr)) {
+			if (!lapb_validate_nr(lapb, frame->nr)) {
+				lapb->frmr_data = *frame;
+				lapb->frmr_type = LAPB_FRMR_Z;
+				lapb_transmit_frmr(lapb);
 #if LAPB_DEBUG > 0
-				printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+				printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
 #endif
-				lapb_establish_data_link(lapb);
-				lapb->state = LAPB_STATE_1;
+				lapb->state     = LAPB_STATE_4;
+				lapb->t1timer   = lapb->t1;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
 				break;
 			}
 			if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) {
-				lapb_frames_acked(lapb, nr);
+				lapb_frames_acked(lapb, frame->nr);
 			} else {
-				lapb_check_iframes_acked(lapb, nr);
+				lapb_check_iframes_acked(lapb, frame->nr);
 			}
-			if (ns == lapb->vr) {
+			if (frame->ns == lapb->vr) {
 				lapb->vr = (lapb->vr + 1) % modulus;
 				queued = lapb_data_indication(lapb, skb);
 				lapb->condition &= ~LAPB_REJECT_CONDITION;
-				if (pf) {
+				if (frame->pf) {
 					lapb_enquiry_response(lapb);
 				} else {
 					if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) {
@@ -459,31 +479,47 @@
 				}
 			} else {
 				if (lapb->condition & LAPB_REJECT_CONDITION) {
-					if (pf)
+					if (frame->pf)
 						lapb_enquiry_response(lapb);
 				} else {
 #if LAPB_DEBUG > 1
-					printk(KERN_DEBUG "lapb: (%p) S3 TX REJ(%d) R%d\n", lapb->token, pf, lapb->vr);
+					printk(KERN_DEBUG "lapb: (%p) S3 TX REJ(%d) R%d\n", lapb->token, frame->pf, lapb->vr);
 #endif
 					lapb->condition |= LAPB_REJECT_CONDITION;
-					lapb_send_control(lapb, LAPB_REJ, pf, LAPB_RESPONSE);
+					lapb_send_control(lapb, LAPB_REJ, frame->pf, LAPB_RESPONSE);
 					lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
 				}
 			}
 			break;
 
 		case LAPB_FRMR:
-		case LAPB_ILLEGAL:
 #if LAPB_DEBUG > 1
-			printk(KERN_DEBUG "lapb: (%p) S3 RX {FRMR,ILLEGAL}(%d)\n", lapb->token, pf);
+			printk(KERN_DEBUG "lapb: (%p) S3 RX FRMR(%d) %02X %02X %02X %02X %02X\n", lapb->token, frame->pf, skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4]);
 #endif
+			lapb_establish_data_link(lapb);
 #if LAPB_DEBUG > 0
 			printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
 #endif
-			lapb_establish_data_link(lapb);
+			lapb_requeue_frames(lapb);
 			lapb->state = LAPB_STATE_1;
 			break;
 
+		case LAPB_ILLEGAL:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S3 RX ILLEGAL(%d)\n", lapb->token, frame->pf);
+#endif
+			lapb->frmr_data = *frame;
+			lapb->frmr_type = LAPB_FRMR_W;
+			lapb_transmit_frmr(lapb);
+#if LAPB_DEBUG > 0
+			printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
+#endif
+			lapb->state     = LAPB_STATE_4;
+			lapb->t1timer   = lapb->t1;
+			lapb->t2timer   = 0;
+			lapb->n2count   = 0;
+			break;
+
 		default:
 			break;
 	}
@@ -493,32 +529,103 @@
 }
 
 /*
+ *	State machine for state 4, Frame Reject State.
+ *	The handling of the timer(s) is in file lapb_timer.c.
+ */
+static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
+{
+	switch (frame->type) {
+		case LAPB_SABM:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX SABM(%d)\n", lapb->token, frame->pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
+#endif
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+				lapb->state     = LAPB_STATE_3;
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+				lapb_connect_indication(lapb, LAPB_OK);
+			}
+			break;
+
+		case LAPB_SABME:
+#if LAPB_DEBUG > 1
+			printk(KERN_DEBUG "lapb: (%p) S4 RX SABME(%d)\n", lapb->token, frame->pf);
+#endif
+			if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
+#endif
+				lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+				lapb->state     = LAPB_STATE_3;
+				lapb->condition = 0x00;
+				lapb->t1timer   = 0;
+				lapb->t2timer   = 0;
+				lapb->n2count   = 0;
+				lapb->vs        = 0;
+				lapb->vr        = 0;
+				lapb->va        = 0;
+				lapb_connect_indication(lapb, LAPB_OK);
+			} else {
+#if LAPB_DEBUG > 1
+				printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+				lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	kfree_skb(skb, FREE_READ);
+}
+
+/*
  *	Process an incoming LAPB frame
  */
 void lapb_data_input(lapb_cb *lapb, struct sk_buff *skb)
 {
-	int frametype, ns, nr, pf, type;
-
-	del_timer(&lapb->timer);
+	struct lapb_frame frame;
 
-	frametype = lapb_decode(lapb, skb, &ns, &nr, &pf, &type);
+	lapb_decode(lapb, skb, &frame);
 
 	switch (lapb->state) {
 		case LAPB_STATE_0:
-			lapb_state0_machine(lapb, skb, frametype, pf);
+			lapb_state0_machine(lapb, skb, &frame);
 			break;
 		case LAPB_STATE_1:
-			lapb_state1_machine(lapb, skb, frametype, pf);
+			lapb_state1_machine(lapb, skb, &frame);
 			break;
 		case LAPB_STATE_2:
-			lapb_state2_machine(lapb, skb, frametype, pf);
+			lapb_state2_machine(lapb, skb, &frame);
 			break;
 		case LAPB_STATE_3:
-			lapb_state3_machine(lapb, skb, frametype, ns, nr, pf, type);
+			lapb_state3_machine(lapb, skb, &frame);
+			break;
+		case LAPB_STATE_4:
+			lapb_state4_machine(lapb, skb, &frame);
 			break;
 	}
-
-	lapb_set_timer(lapb);
 }
 
 #endif
diff -u --recursive --new-file v2.1.26/linux/net/lapb/lapb_subr.c linux/net/lapb/lapb_subr.c
--- v2.1.26/linux/net/lapb/lapb_subr.c	Tue Feb  4 06:44:26 1997
+++ linux/net/lapb/lapb_subr.c	Fri Feb 21 14:58:40 1997
@@ -122,11 +122,9 @@
  *	This routine is the centralised routine for parsing the control
  *	information for the different frame formats.
  */
-int lapb_decode(lapb_cb *lapb, struct sk_buff *skb, int *ns, int *nr, int *pf, int *type)
+void lapb_decode(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
 {
-	int frametype = LAPB_ILLEGAL;
-
-	*ns = *nr = *pf = *type = 0;
+	frame->type = LAPB_ILLEGAL;
 
 #if LAPB_DEBUG > 2
 	printk(KERN_DEBUG "lapb: (%p) S%d RX %02X %02X %02X\n", lapb->token, lapb->state, skb->data[0], skb->data[1], skb->data[2]);
@@ -135,26 +133,26 @@
 	if (lapb->mode & LAPB_MLP) {
 		if (lapb->mode & LAPB_DCE) {
 			if (skb->data[0] == LAPB_ADDR_D)
-				*type = LAPB_COMMAND;
+				frame->cr = LAPB_COMMAND;
 			if (skb->data[0] == LAPB_ADDR_C)
-				*type = LAPB_RESPONSE;
+				frame->cr = LAPB_RESPONSE;
 		} else {
 			if (skb->data[0] == LAPB_ADDR_C)
-				*type = LAPB_COMMAND;
+				frame->cr = LAPB_COMMAND;
 			if (skb->data[0] == LAPB_ADDR_D)
-				*type = LAPB_RESPONSE;
+				frame->cr = LAPB_RESPONSE;
 		}
 	} else {
 		if (lapb->mode & LAPB_DCE) {
 			if (skb->data[0] == LAPB_ADDR_B)
-				*type = LAPB_COMMAND;
+				frame->cr = LAPB_COMMAND;
 			if (skb->data[0] == LAPB_ADDR_A)
-				*type = LAPB_RESPONSE;
+				frame->cr = LAPB_RESPONSE;
 		} else {
 			if (skb->data[0] == LAPB_ADDR_A)
-				*type = LAPB_COMMAND;
+				frame->cr = LAPB_COMMAND;
 			if (skb->data[0] == LAPB_ADDR_B)
-				*type = LAPB_RESPONSE;
+				frame->cr = LAPB_RESPONSE;
 		}
 	}
 		
@@ -162,46 +160,53 @@
 
 	if (lapb->mode & LAPB_EXTENDED) {
 		if ((skb->data[0] & LAPB_S) == 0) {
-			frametype = LAPB_I;			/* I frame - carries NR/NS/PF */
-			*ns = (skb->data[0] >> 1) & 0x7F;
-			*nr = (skb->data[1] >> 1) & 0x7F;
-			*pf = skb->data[1] & LAPB_EPF;
+			frame->type       = LAPB_I;			/* I frame - carries NR/NS/PF */
+			frame->ns         = (skb->data[0] >> 1) & 0x7F;
+			frame->nr         = (skb->data[1] >> 1) & 0x7F;
+			frame->pf         = skb->data[1] & LAPB_EPF;
+			frame->control[0] = skb->data[0];
+			frame->control[1] = skb->data[1];
 			skb_pull(skb, 2);
 		} else if ((skb->data[0] & LAPB_U) == 1) { 	/* S frame - take out PF/NR */
-			frametype = skb->data[0] & 0x0F;
-			*nr = (skb->data[1] >> 1) & 0x7F;
-			*pf = skb->data[1] & LAPB_EPF;
+			frame->type       = skb->data[0] & 0x0F;
+			frame->nr         = (skb->data[1] >> 1) & 0x7F;
+			frame->pf         = skb->data[1] & LAPB_EPF;
+			frame->control[0] = skb->data[0];
+			frame->control[1] = skb->data[1];
 			skb_pull(skb, 2);
 		} else if ((skb->data[0] & LAPB_U) == 3) { 	/* U frame - take out PF */
-			frametype = skb->data[0] & ~LAPB_SPF;
-			*pf = skb->data[0] & LAPB_SPF;
+			frame->type       = skb->data[0] & ~LAPB_SPF;
+			frame->pf         = skb->data[0] & LAPB_SPF;
+			frame->control[0] = skb->data[0];
+			frame->control[1] = 0x00;
 			skb_pull(skb, 1);
 		}
 	} else {
 		if ((skb->data[0] & LAPB_S) == 0) {
-			frametype = LAPB_I;			/* I frame - carries NR/NS/PF */
-			*ns = (skb->data[0] >> 1) & 0x07;
-			*nr = (skb->data[0] >> 5) & 0x07;
-			*pf = skb->data[0] & LAPB_SPF;
+			frame->type = LAPB_I;			/* I frame - carries NR/NS/PF */
+			frame->ns   = (skb->data[0] >> 1) & 0x07;
+			frame->nr   = (skb->data[0] >> 5) & 0x07;
+			frame->pf   = skb->data[0] & LAPB_SPF;
 		} else if ((skb->data[0] & LAPB_U) == 1) { 	/* S frame - take out PF/NR */
-			frametype = skb->data[0] & 0x0F;
-			*nr = (skb->data[0] >> 5) & 0x07;
-			*pf = skb->data[0] & LAPB_SPF;
+			frame->type = skb->data[0] & 0x0F;
+			frame->nr   = (skb->data[0] >> 5) & 0x07;
+			frame->pf   = skb->data[0] & LAPB_SPF;
 		} else if ((skb->data[0] & LAPB_U) == 3) { 	/* U frame - take out PF */
-			frametype = skb->data[0] & ~LAPB_SPF;
-			*pf = skb->data[0] & LAPB_SPF;
+			frame->type = skb->data[0] & ~LAPB_SPF;
+			frame->pf   = skb->data[0] & LAPB_SPF;
 		}
 
+		frame->control[0] = skb->data[0];
+
 		skb_pull(skb, 1);
 	}
-
-	return frametype;
 }
 
 /* 
  *	This routine is called when the HDLC layer internally  generates a
  *	command or  response  for  the remote machine ( eg. RR, UA etc. ). 
- *	Only supervisory or unnumbered frames are processed.
+ *	Only supervisory or unnumbered frames are processed, FRMRs are handled
+ *	by lapb_transmit_frmr below.
  */
 void lapb_send_control(lapb_cb *lapb, int frametype, int poll_bit, int type)
 {
@@ -213,27 +218,74 @@
 
 	skb_reserve(skb, LAPB_HEADER_LEN + 1);
 
-	/* Assume a response - address structure for DTE */
 	if (lapb->mode & LAPB_EXTENDED) {
 		if ((frametype & LAPB_U) == LAPB_U) {
-			dptr = skb_put(skb, 1);
-			*dptr = frametype;
+			dptr   = skb_put(skb, 1);
+			*dptr  = frametype;
 			*dptr |= (poll_bit) ? LAPB_SPF : 0;
 		} else {
-			dptr = skb_put(skb, 2);
-			dptr[0] = frametype;
-			dptr[1] = (lapb->vr << 1);
+			dptr     = skb_put(skb, 2);
+			dptr[0]  = frametype;
+			dptr[1]  = (lapb->vr << 1);
 			dptr[1] |= (poll_bit) ? LAPB_EPF : 0;
 		}
 	} else {
-		dptr = skb_put(skb, 1);
-		*dptr = frametype;
+		dptr   = skb_put(skb, 1);
+		*dptr  = frametype;
 		*dptr |= (poll_bit) ? LAPB_SPF : 0;
 		if ((frametype & LAPB_U) == LAPB_S)		/* S frames carry NR */
 			*dptr |= (lapb->vr << 5);
 	}
 
 	lapb_transmit_buffer(lapb, skb, type);
+}
+
+/* 
+ *	This routine generates FRMRs based on information previously stored in
+ *	the LAPB control block.
+ */
+void lapb_transmit_frmr(lapb_cb *lapb)
+{
+	struct sk_buff *skb;
+	unsigned char  *dptr;
+
+	if ((skb = alloc_skb(LAPB_HEADER_LEN + 7, GFP_ATOMIC)) == NULL)
+		return;
+
+	skb_reserve(skb, LAPB_HEADER_LEN + 1);
+
+	if (lapb->mode & LAPB_EXTENDED) {
+		dptr    = skb_put(skb, 6);
+		*dptr++ = LAPB_FRMR;
+		*dptr++ = lapb->frmr_data.control[0];
+		*dptr++ = lapb->frmr_data.control[1];
+		*dptr++ = (lapb->vs << 1) & 0xFE;
+		*dptr   = (lapb->vr << 1) & 0xFE;
+		if (lapb->frmr_data.cr == LAPB_RESPONSE)
+			*dptr |= 0x01;
+		dptr++;
+		*dptr++ = lapb->frmr_type;
+
+#if LAPB_DEBUG > 1
+	printk(KERN_DEBUG "lapb: (%p) S%d TX FRMR %02X %02X %02X %02X %02X\n", lapb->token, lapb->state, skb->data[1], skb->data[2], skb->data[3], skb->data[4], skb->data[5]);
+#endif
+	} else {
+		dptr    = skb_put(skb, 4);
+		*dptr++ = LAPB_FRMR;
+		*dptr++ = lapb->frmr_data.control[0];
+		*dptr   = (lapb->vs << 1) & 0x0E;
+		*dptr  |= (lapb->vr << 5) & 0xE0;
+		if (lapb->frmr_data.cr == LAPB_RESPONSE)
+			*dptr |= 0x10;
+		dptr++;
+		*dptr++ = lapb->frmr_type;
+
+#if LAPB_DEBUG > 1
+	printk(KERN_DEBUG "lapb: (%p) S%d TX FRMR %02X %02X %02X\n", lapb->token, lapb->state, skb->data[1], skb->data[2], skb->data[3]);
+#endif
+	}
+
+	lapb_transmit_buffer(lapb, skb, LAPB_RESPONSE);
 }
 
 #endif
diff -u --recursive --new-file v2.1.26/linux/net/lapb/lapb_timer.c linux/net/lapb/lapb_timer.c
--- v2.1.26/linux/net/lapb/lapb_timer.c	Tue Feb  4 06:44:26 1997
+++ linux/net/lapb/lapb_timer.c	Fri Feb 21 14:58:40 1997
@@ -180,6 +180,24 @@
 				lapb_requeue_frames(lapb);
 			}
 			break;
+
+		/*
+		 *	Frame reject state, restransmit FRMR frames, up to N2 times.
+		 */
+		case LAPB_STATE_4:
+			if (lapb->n2count == lapb->n2) {
+				lapb_clear_queues(lapb);
+				lapb->state   = LAPB_STATE_0;
+				lapb->t2timer = 0;
+				lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+				printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token);
+#endif
+			} else {
+				lapb->n2count++;
+				lapb_transmit_frmr(lapb);
+			}
+			break;
 	}
 
 	lapb->t1timer = lapb->t1;
diff -u --recursive --new-file v2.1.26/linux/net/netbeui/af_netbeui.c linux/net/netbeui/af_netbeui.c
--- v2.1.26/linux/net/netbeui/af_netbeui.c	Wed Dec 31 16:00:00 1969
+++ linux/net/netbeui/af_netbeui.c	Tue Feb 25 17:12:51 1997
@@ -0,0 +1,658 @@
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/route.h>
+#include <linux/inet.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/termios.h>	/* For TIOCOUTQ/INQ */
+#include <linux/poll.h>
+#include <net/datalink.h>
+#include <net/p8022.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+
+
+#undef NETBEUI_DEBUG
+
+
+#ifdef NETBEUI_DEBUG
+#define DPRINT(x)		print(x)
+#else
+#define DPRINT(x)
+#endif
+
+#define min(a,b)	(((a)<(b))?(a):(b))
+
+/***********************************************************************************************************************\
+*															*
+*						Handlers for the socket list.						*
+*															*
+\***********************************************************************************************************************/
+
+static netbeui_socket *netbeui_socket_list=NULL;
+
+/*
+ *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
+ *	handler using this technique. They can be added although we do not
+ *	use this facility.
+ */
+
+extern inline void netbeui_remove_socket(netbeui_socket *sk)
+{
+	sklist_remove_socket(&netbeui_socket_list,sk);
+}
+
+extenr inline void netbeui_insert_socket(netbeui_socket *sk)
+{
+	sklist_insert_socket(&netbeui_socket_list,sk);
+	netbeui_socket_list=sk;
+	restore_flags(flags);
+}
+
+static void netbeui_destroy_socket(netbeui_socket *sk)
+{
+	/*
+	 *	Release netbios logical channels first
+	 */
+	if(sk->af_nb.nb_link)
+	{
+		netbeui_delete_channel(sk->af_nb.nb_link);
+		sk->af_nb.nb_link=NULL;
+	}
+	if(sk->af_nb.src_name)
+	{
+		netbeui_release_name(sk->af_nb.src_name);
+		sk->af_nb.src_name=NULL;
+	}
+	if(sk->af_nb.dst_name)
+	{
+		netbeui_release_name(sk->af_nb.dst_name);
+		sk->af_nb.dst_name=NULL;
+	}
+	netbeui_remove_listener(sk);
+	sklist_destroy_socket(&netbeui_socket,sk);
+}
+
+/*
+ *	Called from proc fs
+ */
+
+int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	return 0;
+}
+
+/*
+ *	A device event has occurred. Watch for devices going down and
+ *	delete our use of them (iface and route).
+ */
+
+static int nb_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	if(event==NETDEV_DOWN)
+	{
+		/* Discard any use of this */
+		netbeui_drop_device((struct device *)ptr);
+	}
+	return NOTIFY_DONE;
+}
+
+/*******************************************************************************************************************\
+*													            *
+*	      Handling for system calls applied via the various interfaces to a netbeui socket object		    *
+*														    *
+\*******************************************************************************************************************/
+
+static int netbeui_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk=(netbeui_socket *)sock->data;
+	if(sk->state!=TCP_CLOSED)
+		return -EINVAL;
+	if(backlog<0)
+		return -EINVAL;
+	if(backlog<128)
+		sk->backlog=backlog;
+	else
+		sk->backlog=128;
+	sk->state=TCP_LISTEN;
+	sk->state_change(sk);
+	netbeui_add_listener(sk);
+	return 0;
+}
+
+/*
+ *	Create a socket. Initialise the socket, blank the addresses
+ *	set the state.
+ */
+
+static int netbeui_create(struct socket *sock, int protocol)
+{
+	netbeui_socket *sk;
+	sk=(netbeui_socket *)sk_alloc(GFP_KERNEL);
+	if(sk==NULL)
+		return(-ENOBUFS);
+	switch(sock->type)
+	{
+		case SOCK_DGRAM:
+			break;
+		case SOCK_SEQPACKET:
+			break;
+		default:
+			sk_free((void *)sk);
+			return(-ESOCKTNOSUPPORT);
+	}
+
+	MOD_INC_USE_COUNT;
+
+	sock_init_data(sock,sk);
+	sk->mtu=1500;
+	return(0);
+}
+
+/*
+ *	Copy a socket. No work needed.
+ */
+
+static int netbeui_dup(struct socket *newsock,struct socket *oldsock)
+{
+	return(netbeui_create(newsock,oldsock->type));
+}
+
+/*
+ *	Free a socket. No work needed
+ */
+
+static int netbeui_release(struct socket *sock, struct socket *peer)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	if(sk==NULL)
+		return(0);
+	if(!sk->dead)
+		sk->state_change(sk);
+	sk->dead=1;
+	sock->data=NULL;
+	netbeui_destroy_socket(sk);
+	return(0);
+}
+
+/*
+ *	Set the address 'our end' of the connection.
+ */
+
+static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_len)
+{
+	netbeui_socket *sk;
+	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
+	int err;
+
+	sk=(netbeui_socket *)sock->data;
+
+	if(sk->zapped==0)
+		return(-EINVAL);
+
+	if(addr_len!=sizeof(struct sockaddr_netbeui))
+		return -EINVAL;
+
+	if(addr->snb_family!=AF_NETBEUI)
+		return -EAFNOSUPPORT;
+
+	/*
+	 *	This will sleep. To meet POSIX it is non interruptible.
+	 *	Someone should give the 1003.1g authors an injection of
+	 *	imagination...
+	 */
+	 
+	if(sk->af_nb.src_name!=NULL)
+		return -EINVAL;
+	
+	/*
+	 *	Try and get the name. It may return various 'invalid' name
+	 *	problem reports or EADDRINUSE if we or another node holds
+	 *	the desired name.
+	 */
+	 	
+	sk->af_nb.src_name=netbeui_alloc_name(addr, &err);
+	if(sk->af_nb.src_name==NULL)
+		return err;
+	/*
+	 *	Add us to the active socket list 
+	 */
+	netbeui_insert_socket(sk);
+	sk->zapped=0;
+	return(0);
+}
+
+/*
+ *	Set the address we talk to.
+ */
+
+static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr,
+	size_t addr_len, int flags)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
+
+	/*
+	 *	Check pending operations
+	 */	
+	
+	if(sk->state==TCP_ESTABLISHED && sock->state == SS_CONNECTING) 
+	{
+		sock->state==SS_CONNECTED;
+		return 0;
+	}
+	
+	if(sk->state == TCP_CLOSE & sock->state == SS_CONNECTING)
+	{
+		sock->state==SS_UNCONNECTED;
+		return -ECONNREFUSED;
+	}
+	
+	if(sock->state == SS_CONNECTING && (flags & O_NONBLOCK))
+		return -EINPROGRESS;
+	
+	if(sk->state==TCP_ESTABLISHED)
+		return -EISCONN;	 
+	
+	/*
+	 *	If this is new it must really be new...
+	 */
+	 
+	if(sk->af_nb.dst_name==NULL)
+	{
+		if(addr_len != sizeof(struct sockaddr_nb))
+			return -EINVAL;
+		if(addr->snb_family!=AF_NETBEUI)
+			return -EAFNOSUPPORT;
+		/*
+		 *	Try and find the name
+		 */
+	}
+}
+
+/*
+ *	Not relevant
+ */
+
+static int netbeui_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	return(-EOPNOTSUPP);
+}
+
+/*
+ *	WRITE ME
+ */
+
+static int netbeui_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	if(newsock->data)
+		sk_free(newsock->data);
+	return -EOPNOTSUPP;
+}
+
+/*
+ *	Find the name of a netbeui socket. Just copy the right
+ *	fields into the sockaddr.
+ */
+
+static int netbeui_getname(struct socket *sock, struct sockaddr *uaddr,
+	size_t *uaddr_len, int peer)
+{
+	struct sockaddr_netbeui snb;
+	netbeui_socket *sk;
+
+	sk=(netbeui_socket *)sock->data;
+	if(sk->zapped)
+	{
+		return -EINVAL;
+	}
+
+	*uaddr_len = sizeof(struct sockaddr_netbeui);
+
+	if(peer)
+	{
+		if(sk->state!=TCP_ESTABLISHED)
+			return -ENOTCONN;
+	}
+	else
+	{
+	}
+	snb.snb_family = AF_NETBEUI;
+	memcpy(uaddr,&snb,sizeof(snb));
+	return(0);
+}
+
+/*
+ *	Receive a packet (in skb) from device dev.
+ */
+
+static int netbeui_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	return nb_llc_rcv(skb);
+}
+
+static int netbeui_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	struct sockaddr_nb *usnb=(struct sockaddr_nb *)msg->msg_name;
+	struct sk_buff *skb;
+	struct device *dev;
+	struct nbhdr *nbp;
+	int size;
+	struct netbeui_route *rt;
+	int loopback=0;
+	int err;
+
+	if(flags)
+		return -EINVAL;
+
+	if(len>1500)	/* - headers!! */
+		return -EMSGSIZE;
+
+	if(usnb)
+	{
+		if(sk->zapped)
+		{
+			if(netbeui_autobind(sk)<0)
+				return -EBUSY;
+		}
+
+		if(msg->msg_namelen <sizeof(*usnb))
+			return(-EINVAL);
+		if(usnb->snb_family != AF_NETBEUI)
+			return -EINVAL;
+		/* Check broadcast */
+	}
+	else
+	{
+		if(sk->state!=TCP_ESTABLISHED)
+			return -ENOTCONN;
+		/* Connected .. */
+	}
+
+	/* Build a packet */
+	SOCK_DEBUG(sk, "SK %p: Got address.\n",sk);
+	size=sizeof(struct nbhdr)+len+nb_dl->header_length;	/* For headers */
+
+	SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", sk, size, dev->name);
+	size += dev->hard_header_len;
+	skb = sock_alloc_send_skb(sk, size, 0, 0 , &err);
+	if(skb==NULL)
+		return err;
+
+	skb->sk=sk;
+	skb->free=1;
+	skb->arp=1;
+	skb_reserve(skb,nb_dl->header_length);
+	skb_reserve(skb,dev->hard_header_len);
+	skb->dev=dev;
+	SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
+	nbp=(struct nbhdr *)skb_put(skb,sizeof(struct nbhdr));
+	SOCK_DEBUG(sk, "SK %p: Copy user data (%d bytes).\n", sk, len);
+	err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len);
+	if (err)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return -EFAULT;
+	}
+
+#ifdef CONFIG_FIREWALL
+
+	if(call_out_firewall(AF_NETBEUI, skb->dev, nbp, NULL)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return -EPERM;
+	}
+
+#endif
+
+	if(nb_send_low(dev,skb,&usat->sat_addr, NULL)==-1)
+		kfree_skb(skb, FREE_WRITE);
+	SOCK_DEBUG(sk, "SK %p: Done write (%d).\n", sk, len);
+	return len;
+}
+
+
+static int netbeui_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	struct sockaddr_nb *snb=(struct sockaddr_nb *)msg->msg_name;
+	struct nbphdr	*nbp = NULL;
+	int copied = 0;
+	struct sk_buff *skb;
+	int er = 0;
+
+	if(addr_len)
+		*addr_len=sizeof(*snb);
+
+	skb=skb_recv_datagram(sk,flags,noblock,&er);
+	if(skb==NULL)
+		return er;
+
+	snb = (struct nbphdr *)(skb->h.raw);
+	if(sk->type==SOCK_RAW)
+	{
+		copied=skb->len
+		if(copied > size)
+		{
+			copied=size;
+			msg->msg_flags|=MSG_TRUNC;
+		}
+		er = skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied);
+		if (er)
+			goto out;
+	}
+	else
+	{
+		copied=skb->len - sizeof(*nbp);
+		if (copied > size)
+		{
+			copied = size;
+			msg->msg_flags|=MSG_TRUNC;
+		}
+		er = skb_copy_datagram_iovec(skb,sizeof(*nbp),msg->msg_iov,copied);
+		if (er)
+			goto out;
+	}
+	if(snb)
+	{
+		sat->sat_family=AF_NETBEUI;
+		/* Copy name over */
+	}
+out:
+	skb_free_datagram(sk, skb);
+	return er ? er : (copied);
+}
+
+
+static int netbeui_shutdown(struct socket *sk,int how)
+{
+	return -EOPNOTSUPP;
+}
+
+static int netbeui_poll(struct socket *sock, poll_table *wait)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+
+	return datagram_poll(sk,wait);
+}
+
+/*
+ *	Netbeui ioctl calls.
+ */
+
+static int netbeui_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
+{
+	long amount=0;
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+
+	switch(cmd)
+	{
+		/*
+		 *	Protocol layer
+		 */
+		case TIOCOUTQ:
+			amount=sk->sndbuf-sk->wmem_alloc;
+			if(amount<0)
+				amount=0;
+			break;
+		case TIOCINQ:
+		{
+			struct sk_buff *skb;
+			/* These two are safe on a single CPU system as only user tasks fiddle here */
+			if((skb=skb_peek(&sk->receive_queue))!=NULL)
+				amount=skb->len-sizeof(struct ddpehdr);
+			break;
+		}
+		case SIOCGSTAMP:
+			if (sk)
+			{
+				if(sk->stamp.tv_sec==0)
+					return -ENOENT;
+				return copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)) ? -EFAULT : 0;
+			}
+			return -EINVAL;
+		/*
+		 *	Routing
+		 */
+		case SIOCADDRT:
+		case SIOCDELRT:
+			if(!suser())
+				return -EPERM;
+			return(nbrtr_ioctl(cmd,(void *)arg));
+		/*
+		 *	Interface
+		 */
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFBRDADDR:
+			return nbif_ioctl(cmd,(void *)arg);
+		/*
+		 *	Physical layer ioctl calls
+		 */
+		case SIOCSIFLINK:
+		case SIOCGIFHWADDR:
+		case SIOCSIFHWADDR:
+		case SIOCGIFFLAGS:
+		case SIOCSIFFLAGS:
+		case SIOCGIFMTU:
+		case SIOCGIFCONF:
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
+
+			return(dev_ioctl(cmd,(void *) arg));
+
+		case SIOCSIFMETRIC:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFMEM:
+		case SIOCSIFMEM:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+			return -EINVAL;
+
+		default:
+			return -EINVAL;
+	}
+	return put_user(amount, (int *)arg);
+}
+
+static struct proto_ops netbeui_proto_ops = {
+	AF_NETBEUI,
+
+	netbeui_create,
+	netbeui_dup,
+	netbeui_release,
+	netbeui_bind,
+	netbeui_connect,
+	netbeui_socketpair,
+	netbeui_accept,
+	netbeui_getname,
+	netbeui_poll,
+	netbeui_ioctl,
+	netbeui_listen,
+	netbeui_shutdown,
+	sock_no_setsockopt,
+	sock_no_getsockopt,
+	sock_no_fcntl,
+	netbeui_sendmsg,
+	netbeui_recvmsg
+};
+
+static struct notifier_block nb_notifier={
+	nb_device_event,
+	NULL,
+	0
+};
+
+static char nb_snap_id[]={0x08,0x00,0x07,0x80,0x9B};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_netbeui = {
+	PROC_NET_NETBEUI, 9, "netbeui",
+	S_IFREG | S_IRUGO, 1, 0, 0
+	0, &proc_net_inode_operations,
+	netbeui_get_info
+};
+#endif
+
+/* Called by proto.c on kernel start up */
+
+void netbeui_proto_init(struct net_proto *pro)
+{
+	(void) sock_register(netbeui_proto_ops.family, &netbeui_proto_ops);
+	if ((nb_dl = register_8022_client(nb_8022_id, netbeui_rcv)) == NULL)
+		printk(KERN_CRIT "Unable to register Netbeui with 802.2.\n");
+
+	register_netdevice_notifier(&nb_notifier);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_netbeui);
+#endif
+
+	printk(KERN_INFO "NetBEUI 0.03 for Linux NET3.037\n");
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	netbeui_proto_init(NULL);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unsigned long flags;
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_NETBEUI);
+#endif
+	unregister_netdevice_notifier(&nb_notifier);
+	unregister_snap_client(nb_snap_id);
+	sock_unregister(netbeui_proto_ops.family);
+}
+
+#endif  /* MODULE */
diff -u --recursive --new-file v2.1.26/linux/net/netbeui/netbeui.c linux/net/netbeui/netbeui.c
--- v2.1.26/linux/net/netbeui/netbeui.c	Mon Jan 27 01:04:48 1997
+++ linux/net/netbeui/netbeui.c	Wed Dec 31 16:00:00 1969
@@ -1,861 +0,0 @@
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/if_ether.h>
-#include <linux/route.h>
-#include <linux/inet.h>
-#include <linux/notifier.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <linux/termios.h>	/* For TIOCOUTQ/INQ */
-#include <linux/poll.h>
-#include <net/datalink.h>
-#include <net/p8022.h>
-#include <net/psnap.h>
-#include <net/sock.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/firewall.h>
-
-
-#undef NETBEUI_DEBUG
-
-
-#ifdef NETBEUI_DEBUG
-#define DPRINT(x)		print(x)
-#else
-#define DPRINT(x)
-#endif
-
-#define min(a,b)	(((a)<(b))?(a):(b))
-
-/***********************************************************************************************************************\
-*															*
-*						Handlers for the socket list.						*
-*															*
-\***********************************************************************************************************************/
-
-static netbeui_socket *netbeui_socket_list=NULL;
-
-/*
- *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
- *	handler using this technique. They can be added although we do not
- *	use this facility.
- */
-
-extern inline void netbeui_remove_socket(netbeui_socket *sk)
-{
-	sklist_remove_socket(&netbeui_socket_list,sk);
-}
-
-extenr inline void netbeui_insert_socket(netbeui_socket *sk)
-{
-	sklist_insert_socket(&netbeui_socket_list,sk);
-	netbeui_socket_list=sk;
-	restore_flags(flags);
-}
-
-static void netbeui_destroy_socket(netbeui_socket *sk)
-{
-	/*
-	 *	Release netbios logical channels first
-	 */
-	if(sk->af_nb.nb_link)
-	{
-		netbeui_delete_channel(sk->af_nb.nb_link);
-		sk->af_nb.nb_link=NULL;
-	}
-	if(sk->af_nb.src_name)
-	{
-		netbeui_release_name(sk->af_nb.src_name);
-		sk->af_nb.src_name=NULL;
-	}
-	if(sk->af_nb.dst_name)
-	{
-		netbeui_release_name(sk->af_nb.dst_name);
-		sk->af_nb.dst_name=NULL;
-	}
-	netbeui_remove_listener(sk);
-	sklist_destroy_socket(&netbeui_socket,sk);
-}
-
-/*
- *	Called from proc fs
- */
-
-int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	netbeui_socket *s;
-	int len=0;
-	off_t pos=0;
-	off_t begin=0;
-
-	/*
-	 *	Output the netbeui data for the /proc virtual fs.
-	 */
-
-	len += sprintf (buffer,"Type local_addr  remote_addr tx_queue rx_queue st uid\n");
-	for (s = netbeui_socket_list; s != NULL; s = s->next)
-	{
-		len += sprintf (buffer+len,"%02X   ", s->type);
-		len += sprintf (buffer+len,"%s  ",
-			s->af_nb.src_name->text);
-		len += sprintf (buffer+len,"%s  ",
-			s->af_nb.dst_name->text);
-		len += sprintf (buffer+len,"%08X:%08X ", s->wmem_alloc, s->rmem_alloc);
-		len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
-
-		/* Are we still dumping unwanted data then discard the record */
-		pos=begin+len;
-
-		if(pos<offset)
-		{
-			len=0;			/* Keep dumping into the buffer start */
-			begin=pos;
-		}
-		if(pos>offset+length)		/* We have dumped enough */
-			break;
-	}
-
-	/* The data in question runs from begin to begin+len */
-	*start=buffer+(offset-begin);	/* Start of wanted data */
-	len-=(offset-begin);		/* Remove unwanted header data from length */
-	if(len>length)
-		len=length;		/* Remove unwanted tail data from length */
-
-	return len;
-}
-
-/*
- *	A device event has occurred. Watch for devices going down and
- *	delete our use of them (iface and route).
- */
-
-static int nb_device_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
-	if(event==NETDEV_DOWN)
-	{
-		/* Discard any use of this */
-		netbeui_drop_device((struct device *)ptr);
-	}
-	return NOTIFY_DONE;
-}
-
-/*******************************************************************************************************************\
-*													            *
-*	      Handling for system calls applied via the various interfaces to a netbeui socket object		    *
-*														    *
-\*******************************************************************************************************************/
-
-/*
- *	Set 'magic' options for netbeui. If we don't have any this is fine
- *	as it is.
- */
-
-static int netbeui_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
-{
-	netbeui_socket *sk;
-	int err,opt;
-
-	sk=(netbeui_socket *)sock->data;
-
-	if(optval==NULL)
-		return(-EINVAL);
-
-	err = get_user(opt, (int *)optval);
-	if (err)
-		return err;
-
-	switch(level)
-	{
-		case SOL_NETBEUI:
-			switch(optname)
-			{
-				default:
-					return -EOPNOTSUPP;
-			}
-			break;
-
-		default:
-			return -EOPNOTSUPP;
-	}
-}
-
-
-/*
- *	Get any magic options. Comment above applies.
- */
-
-static int netbeui_getsockopt(struct socket *sock, int level, int optname,
-	char *optval, int *optlen)
-{
-	netbeui_socket *sk;
-	int val=0;
-	int err;
-
-	sk=(netbeui_socket *)sock->data;
-
-	switch(level)
-	{
-
-		case SOL_NETBEUI:
-			switch(optname)
-			{
-				default:
-					return -ENOPROTOOPT;
-			}
-			break;
-
-		default:
-			return -EOPNOTSUPP;
-	}
-	err = put_user(sizeof(int),optlen);
-	if (!err)
-		err = put_user(val, (int *) optval);
-	return err;
-}
-
-/*
- *	Only for connection oriented sockets - ignore
- */
-
-static int netbeui_listen(struct socket *sock, int backlog)
-{
-	struct sock *sk=(netbeui_socket *)sock->data;
-	if(sk->state!=TCP_CLOSED)
-		return -EINVAL;
-	if(backlog<0)
-		return -EINVAL;
-	if(backlog<128)
-		sk->backlog=backlog;
-	else
-		sk->backlog=128;
-	sk->state=TCP_LISTEN;
-	sk->state_change(sk);
-	netbeui_add_listener(sk);
-	return 0;
-}
-
-/*
- *	Create a socket. Initialise the socket, blank the addresses
- *	set the state.
- */
-
-static int netbeui_create(struct socket *sock, int protocol)
-{
-	netbeui_socket *sk;
-	sk=(netbeui_socket *)sk_alloc(GFP_KERNEL);
-	if(sk==NULL)
-		return(-ENOBUFS);
-	switch(sock->type)
-	{
-		case SOCK_DGRAM:
-			break;
-		case SOCK_SEQPACKET:
-			break;
-		default:
-			sk_free((void *)sk);
-			return(-ESOCKTNOSUPPORT);
-	}
-
-	MOD_INC_USE_COUNT;
-
-	sock_init_data(sock,sk);
-	sk->mtu=1500;
-	return(0);
-}
-
-/*
- *	Copy a socket. No work needed.
- */
-
-static int netbeui_dup(struct socket *newsock,struct socket *oldsock)
-{
-	return(netbeui_create(newsock,oldsock->type));
-}
-
-/*
- *	Free a socket. No work needed
- */
-
-static int netbeui_release(struct socket *sock, struct socket *peer)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	if(sk==NULL)
-		return(0);
-	if(!sk->dead)
-		sk->state_change(sk);
-	sk->dead=1;
-	sock->data=NULL;
-	netbeui_destroy_socket(sk);
-	return(0);
-}
-
-/*
- *	Set the address 'our end' of the connection.
- */
-
-static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_len)
-{
-	netbeui_socket *sk;
-	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
-	int err;
-
-	sk=(netbeui_socket *)sock->data;
-
-	if(sk->zapped==0)
-		return(-EINVAL);
-
-	if(addr_len!=sizeof(struct sockaddr_netbeui))
-		return -EINVAL;
-
-	if(addr->snb_family!=AF_NETBEUI)
-		return -EAFNOSUPPORT;
-
-	/*
-	 *	This will sleep. To meet POSIX it is non interruptible.
-	 *	Someone should give the 1003.1g authors an injection of
-	 *	imagination...
-	 */
-	 
-	if(sk->af_nb.src_name!=NULL)
-		return -EINVAL;
-	
-	/*
-	 *	Try and get the name. It may return various 'invalid' name
-	 *	problem reports or EADDRINUSE if we or another node holds
-	 *	the desired name.
-	 */
-	 	
-	sk->af_nb.src_name=netbeui_alloc_name(addr, &err);
-	if(sk->af_nb.src_name==NULL)
-		return err;
-	/*
-	 *	Add us to the active socket list 
-	 */
-	netbeui_insert_socket(sk);
-	sk->zapped=0;
-	return(0);
-}
-
-/*
- *	Set the address we talk to.
- */
-
-static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr,
-	size_t addr_len, int flags)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
-
-	/*
-	 *	Check pending operations
-	 */	
-	
-	if(sk->state==TCP_ESTABLISHED && sock->state == SS_CONNECTING) 
-	{
-		sock->state==SS_CONNECTED;
-		return 0;
-	}
-	
-	if(sk->state == TCP_CLOSE & sock->state == SS_CONNECTING)
-	{
-		sock->state==SS_UNCONNECTED;
-		return -ECONNREFUSED;
-	}
-	
-	if(sock->state == SS_CONNECTING && (flags & O_NONBLOCK))
-		return -EINPROGRESS;
-	
-	if(sk->state==TCP_ESTABLISHED)
-		return -EISCONN;	 
-	
-	/*
-	 *	If this is new it must really be new...
-	 */
-	 
-	if(sk->af_nb.dst_name==NULL)
-	{
-		if(addr_len != sizeof(struct sockaddr_nb))
-			return -EINVAL;
-		if(addr->snb_family!=AF_NETBEUI)
-			return -EAFNOSUPPORT;
-		/*
-		 *	Try and find the name
-		 */
-	}
-}
-
-/*
- *	Not relevant
- */
-
-static int netbeui_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	return(-EOPNOTSUPP);
-}
-
-/*
- *	WRITE ME
- */
-
-static int netbeui_accept(struct socket *sock, struct socket *newsock, int flags)
-{
-	if(newsock->data)
-		sk_free(newsock->data);
-	return -EOPNOTSUPP;
-}
-
-/*
- *	Find the name of a netbeui socket. Just copy the right
- *	fields into the sockaddr.
- */
-
-static int netbeui_getname(struct socket *sock, struct sockaddr *uaddr,
-	size_t *uaddr_len, int peer)
-{
-	struct sockaddr_netbeui snb;
-	netbeui_socket *sk;
-
-	sk=(netbeui_socket *)sock->data;
-	if(sk->zapped)
-	{
-		return -EINVAL;
-	}
-
-	*uaddr_len = sizeof(struct sockaddr_netbeui);
-
-	if(peer)
-	{
-		if(sk->state!=TCP_ESTABLISHED)
-			return -ENOTCONN;
-	}
-	else
-	{
-	}
-	snb.snb_family = AF_NETBEUI;
-	memcpy(uaddr,&snb,sizeof(snb));
-	return(0);
-}
-
-/*
- *	Receive a packet (in skb) from device dev.
- */
-
-static int netbeui_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-	netbeui_socket *sock;
-}
-
-static int netbeui_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	struct sockaddr_at *usat=(struct sockaddr_at *)msg->msg_name;
-	struct sockaddr_at local_snetbeui, gsat;
-	struct sk_buff *skb;
-	struct device *dev;
-	struct ddpehdr *ddp;
-	int size;
-	struct netbeui_route *rt;
-	int loopback=0;
-	int err;
-
-	if(flags)
-		return -EINVAL;
-
-	if(len>587)
-		return -EMSGSIZE;
-
-	if(usat)
-	{
-		if(sk->zapped)
-		{
-			if(netbeui_autobind(sk)<0)
-				return -EBUSY;
-		}
-
-		if(msg->msg_namelen <sizeof(*usat))
-			return(-EINVAL);
-		if(usat->sat_family != AF_NETBEUI)
-			return -EINVAL;
-#if 0 	/* netnetbeui doesn't implement this check */
-		if(usat->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
-			return -EPERM;
-#endif
-	}
-	else
-	{
-		if(sk->state!=TCP_ESTABLISHED)
-			return -ENOTCONN;
-		usat=&local_snetbeui;
-		usat->sat_family=AF_NETBEUI;
-		usat->sat_port=sk->protinfo.af_at.dest_port;
-		usat->sat_addr.s_node=sk->protinfo.af_at.dest_node;
-		usat->sat_addr.s_net=sk->protinfo.af_at.dest_net;
-	}
-
-	/* Build a packet */
-	SOCK_DEBUG(sk, "SK %p: Got address.\n",sk);
-	size=sizeof(struct ddpehdr)+len+nb_dl->header_length;	/* For headers */
-
-	if(usat->sat_addr.s_net!=0 || usat->sat_addr.s_node == ATADDR_ANYNODE)
-	{
-		rt=atrtr_find(&usat->sat_addr);
-		if(rt==NULL)
-			return -ENETUNREACH;
-		dev=rt->dev;
-	}
-	else
-	{
-		struct at_addr at_hint;
-		at_hint.s_node=0;
-		at_hint.s_net=sk->protinfo.af_at.src_net;
-		rt=atrtr_find(&at_hint);
-		if(rt==NULL)
-			return -ENETUNREACH;
-		dev=rt->dev;
-	}
-	SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", sk, size, dev->name);
-	size += dev->hard_header_len;
-	skb = sock_alloc_send_skb(sk, size, 0, 0 , &err);
-	if(skb==NULL)
-		return err;
-
-	skb->sk=sk;
-	skb->free=1;
-	skb->arp=1;
-	skb_reserve(skb,nb_dl->header_length);
-	skb_reserve(skb,dev->hard_header_len);
-	skb->dev=dev;
-	SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
-	ddp=(struct ddpehdr *)skb_put(skb,sizeof(struct ddpehdr));
-	ddp->deh_pad=0;
-	ddp->deh_hops=0;
-	ddp->deh_len=len+sizeof(*ddp);
-	/*
-	 *	Fix up the length field	[Ok this is horrible but otherwise
-	 *	I end up with unions of bit fields and messy bit field order
-	 *	compiler/endian dependencies..
-	 */
-	*((__u16 *)ddp)=ntohs(*((__u16 *)ddp));
-
-	ddp->deh_dnet=usat->sat_addr.s_net;
-	ddp->deh_snet=sk->protinfo.af_at.src_net;
-	ddp->deh_dnode=usat->sat_addr.s_node;
-	ddp->deh_snode=sk->protinfo.af_at.src_node;
-	ddp->deh_dport=usat->sat_port;
-	ddp->deh_sport=sk->protinfo.af_at.src_port;
-	SOCK_DEBUG(sk, "SK %p: Copy user data (%d bytes).\n", sk, len);
-	err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len);
-	if (err)
-	{
-		kfree_skb(skb, FREE_WRITE);
-		return -EFAULT;
-	}
-
-	if(sk->no_check==1)
-		ddp->deh_sum=0;
-	else
-		ddp->deh_sum=netbeui_checksum(ddp, len+sizeof(*ddp));
-
-#ifdef CONFIG_FIREWALL
-
-	if(call_out_firewall(AF_NETBEUI, skb->dev, ddp, NULL)!=FW_ACCEPT)
-	{
-		kfree_skb(skb, FREE_WRITE);
-		return -EPERM;
-	}
-
-#endif
-
-	/*
-	 *	Loopback broadcast packets to non gateway targets (ie routes
-	 *	to group we are in)
-	 */
-
-	if(ddp->deh_dnode==ATADDR_BCAST)
-	{
-		if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK)))
-		{
-			struct sk_buff *skb2=skb_clone(skb, GFP_KERNEL);
-			if(skb2)
-			{
-				loopback=1;
-				SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk);
-				if(aarp_send_ddp(dev,skb2,&usat->sat_addr, NULL)==-1)
-					kfree_skb(skb2, FREE_WRITE);
-				/* else queued/sent above in the aarp queue */
-			}
-		}
-	}
-
-	if((dev->flags&IFF_LOOPBACK) || loopback)
-	{
-		SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk);
-		/* loop back */
-		atomic_sub(skb->truesize, &sk->wmem_alloc);
-		nb_dl->datalink_header(nb_dl, skb, dev->dev_addr);
-		skb->sk = NULL;
-		skb->mac.raw=skb->data;
-		skb->h.raw = skb->data + nb_dl->header_length + dev->hard_header_len;
-		skb_pull(skb,dev->hard_header_len);
-		skb_pull(skb,nb_dl->header_length);
-		netbeui_rcv(skb,dev,NULL);
-	}
-	else
-	{
-		SOCK_DEBUG(sk, "SK %p: send out.\n", sk);
-		if ( rt->flags & RTF_GATEWAY ) {
-		    gsat.sat_addr = rt->gateway;
-		    usat = &gsat;
-		}
-
-		if(nb_send_low(dev,skb,&usat->sat_addr, NULL)==-1)
-			kfree_skb(skb, FREE_WRITE);
-		/* else queued/sent above in the aarp queue */
-	}
-	SOCK_DEBUG(sk, "SK %p: Done write (%d).\n", sk, len);
-	return len;
-}
-
-
-static int netbeui_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-	struct sockaddr_at *sat=(struct sockaddr_at *)msg->msg_name;
-	struct ddpehdr	*ddp = NULL;
-	int copied = 0;
-	struct sk_buff *skb;
-	int er = 0;
-
-	if(addr_len)
-		*addr_len=sizeof(*sat);
-
-	skb=skb_recv_datagram(sk,flags,noblock,&er);
-	if(skb==NULL)
-		return er;
-
-	ddp = (struct ddpehdr *)(skb->h.raw);
-	if(sk->type==SOCK_RAW)
-	{
-		copied=ddp->deh_len;
-		if(copied > size)
-		{
-			copied=size;
-			msg->msg_flags|=MSG_TRUNC;
-		}
-		er = skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied);
-		if (er)
-			goto out;
-	}
-	else
-	{
-		copied=ddp->deh_len - sizeof(*ddp);
-		if (copied > size)
-		{
-			copied = size;
-			msg->msg_flags|=MSG_TRUNC;
-		}
-		er = skb_copy_datagram_iovec(skb,sizeof(*ddp),msg->msg_iov,copied);
-		if (er)
-			goto out;
-	}
-	if(sat)
-	{
-		sat->sat_family=AF_NETBEUI;
-		sat->sat_port=ddp->deh_sport;
-		sat->sat_addr.s_node=ddp->deh_snode;
-		sat->sat_addr.s_net=ddp->deh_snet;
-	}
-out:
-	skb_free_datagram(sk, skb);
-	return er ? er : (copied);
-}
-
-
-static int netbeui_shutdown(struct socket *sk,int how)
-{
-	return -EOPNOTSUPP;
-}
-
-static int netbeui_poll(struct socket *sock, poll_table *wait)
-{
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-
-	return datagram_poll(sk,wait);
-}
-
-/*
- *	Netbeui ioctl calls.
- */
-
-static int netbeui_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
-{
-	long amount=0;
-	netbeui_socket *sk=(netbeui_socket *)sock->data;
-
-	switch(cmd)
-	{
-		/*
-		 *	Protocol layer
-		 */
-		case TIOCOUTQ:
-			amount=sk->sndbuf-sk->wmem_alloc;
-			if(amount<0)
-				amount=0;
-			break;
-		case TIOCINQ:
-		{
-			struct sk_buff *skb;
-			/* These two are safe on a single CPU system as only user tasks fiddle here */
-			if((skb=skb_peek(&sk->receive_queue))!=NULL)
-				amount=skb->len-sizeof(struct ddpehdr);
-			break;
-		}
-		case SIOCGSTAMP:
-			if (sk)
-			{
-				if(sk->stamp.tv_sec==0)
-					return -ENOENT;
-				return copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)) ? -EFAULT : 0;
-			}
-			return -EINVAL;
-		/*
-		 *	Routing
-		 */
-		case SIOCADDRT:
-		case SIOCDELRT:
-			if(!suser())
-				return -EPERM;
-			return(atrtr_ioctl(cmd,(void *)arg));
-		/*
-		 *	Interface
-		 */
-		case SIOCGIFADDR:
-		case SIOCSIFADDR:
-		case SIOCGIFBRDADDR:
-			return atif_ioctl(cmd,(void *)arg);
-		/*
-		 *	Physical layer ioctl calls
-		 */
-		case SIOCSIFLINK:
-		case SIOCGIFHWADDR:
-		case SIOCSIFHWADDR:
-		case SIOCGIFFLAGS:
-		case SIOCSIFFLAGS:
-		case SIOCGIFMTU:
-		case SIOCGIFCONF:
-		case SIOCADDMULTI:
-		case SIOCDELMULTI:
-
-			return(dev_ioctl(cmd,(void *) arg));
-
-		case SIOCSIFMETRIC:
-		case SIOCSIFBRDADDR:
-		case SIOCGIFNETMASK:
-		case SIOCSIFNETMASK:
-		case SIOCGIFMEM:
-		case SIOCSIFMEM:
-		case SIOCGIFDSTADDR:
-		case SIOCSIFDSTADDR:
-			return -EINVAL;
-
-		default:
-			return -EINVAL;
-	}
-	return put_user(amount, (int *)arg);
-}
-
-static struct proto_ops netbeui_proto_ops = {
-	AF_NETBEUI,
-
-	netbeui_create,
-	netbeui_dup,
-	netbeui_release,
-	netbeui_bind,
-	netbeui_connect,
-	netbeui_socketpair,
-	netbeui_accept,
-	netbeui_getname,
-	netbeui_poll,
-	netbeui_ioctl,
-	netbeui_listen,
-	netbeui_shutdown,
-	netbeui_setsockopt,
-	netbeui_getsockopt,
-	sock_no_fcntl,
-	netbeui_sendmsg,
-	netbeui_recvmsg
-};
-
-static struct notifier_block nb_notifier={
-	nb_device_event,
-	NULL,
-	0
-};
-
-static char nb_snap_id[]={0x08,0x00,0x07,0x80,0x9B};
-
-#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry proc_netbeui = {
-	PROC_NET_NETBEUI, 9, "netbeui",
-	S_IFREG | S_IRUGO, 1, 0, 0
-	0, &proc_net_inode_operations,
-	netbeui_get_info
-};
-#endif
-
-/* Called by proto.c on kernel start up */
-
-void netbeui_proto_init(struct net_proto *pro)
-{
-	(void) sock_register(netbeui_proto_ops.family, &netbeui_proto_ops);
-/* ddp?  isn't it atalk too? 8) */
-	if ((nb_dl = register_snap_client(nb_snap_id, netbeui_rcv)) == NULL)
-		printk(KERN_CRIT "Unable to register DDP with SNAP.\n");
-
-	register_netdevice_notifier(&nb_notifier);
-
-#ifdef CONFIG_PROC_FS
-	proc_net_register(&proc_netbeui);
-#endif
-
-	printk(KERN_INFO "NetBEUI 0.02 for Linux NET3.037\n");
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	netbeui_proto_init(NULL);
-	return 0;
-}
-
-void cleanup_module(void)
-{
-	unsigned long flags;
-#ifdef CONFIG_PROC_FS
-	proc_net_unregister(PROC_NET_NETBEUI);
-#endif
-	unregister_netdevice_notifier(&nb_notifier);
-	unregister_snap_client(nb_snap_id);
-	sock_unregister(netbeui_proto_ops.family);
-}
-
-#endif  /* MODULE */
diff -u --recursive --new-file v2.1.26/linux/net/netbeui/netbeui_llc.c linux/net/netbeui/netbeui_llc.c
--- v2.1.26/linux/net/netbeui/netbeui_llc.c	Thu Jan  2 05:13:29 1997
+++ linux/net/netbeui/netbeui_llc.c	Tue Feb 25 17:12:51 1997
@@ -70,6 +70,11 @@
 	 *	See what has occured
 	 */
 	 
+
+	/*
+	 *	Connect completion confirmation
+	 */
+	 
 	if(llc->llc_callbacks&LLC_CONN_CONFIRM)
 	{
 		/*
@@ -93,7 +98,13 @@
 	 */
 
 	if(llc->llc_callbacks&LLC_DATA_INDIC)
+	{
 		netbeu_rcv_stream(llc,llc->inc_skb);
+		/*
+		 *	Frame free is controlled by our stream processor
+		 */
+		return;
+	}
 
 	/*
 	 *	We got disconnected
@@ -113,6 +124,10 @@
 		}
 	}
 	
+	/*
+	 *	Miscellaneous burps
+	 */
+	
 	if(llc->llc_callbacks&(LLC_RESET_INDIC_LOC|LLC_RESET_INDIC_REM|
 					LLC_RST_CONFIRM))
 	{
@@ -124,6 +139,10 @@
 		 */
 	}
 
+	/*
+	 *	Track link busy status
+	 */
+	 
 	if(llc->llc_callbacks&LLC_REMOTE_BUSY)
 		nb->busy=1;	/* Send no more for a bit */
 	if(llc->llc_callbacks&LLC_REMOTE_NOTBUSY)
@@ -136,10 +155,15 @@
 	 *	UI frames are passed to the upper netbeui layer.
 	 */
 	if(llc->llc_callbacks&LLC_UI_DATA)
+	{
 		netbeui_rcv_dgram(llc,llc->inc_skb);
+		return;
+	}
 
 	/* We ignore TST, XID, FRMR stuff */
 	/* FIXME: We need to free frames here once I fix the callback! */
+	if(llc->inc_skb)
+		kfree_skb(skb, FREE_READ);
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.26/linux/net/netrom/af_netrom.c	Sun Feb  2 05:18:52 1997
+++ linux/net/netrom/af_netrom.c	Fri Feb 21 14:58:40 1997
@@ -308,7 +308,7 @@
 		kfree_skb(skb, FREE_READ);
 	}
 
-	if (sk->wmem_alloc > 0 || sk->rmem_alloc > 0) {	/* Defer: outstanding buffers */
+	if (sk->wmem_alloc != 0 || sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
 		init_timer(&sk->timer);
 		sk->timer.expires  = jiffies + 10 * HZ;
 		sk->timer.function = nr_destroy_timer;
@@ -421,17 +421,16 @@
 	int err, opt;
 
 	if (level != SOL_NETROM)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
 
-	if (optval == NULL)
+	if(optlen<sizeof(int))
 		return -EINVAL;
+		
+	if(get_user(opt, (int *)optval))
+		return -EFAULT;
 
-	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
-		return err;
-
-	get_user(opt, (int *)optval);
-
-	switch (optname) {
+	switch (optname) 
+	{
 		case NETROM_T1:
 			if (opt < 1)
 				return -EINVAL;
@@ -476,10 +475,13 @@
 {
 	struct sock *sk = sock->sk;
 	int val = 0;
-	int err; 
+	int len; 
 
 	if (level != SOL_NETROM)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
+	
+	if(get_user(len,optlen))
+		return -EFAULT;
 
 	switch (optname) {
 		case NETROM_T1:
@@ -510,16 +512,11 @@
 			return -ENOPROTOOPT;
 	}
 
-	if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
-		return err;
-
-	put_user(sizeof(int), optlen);
-
-	if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
-		return err;
-
-	put_user(val, (int *)optval);
-
+	len=min(len,sizeof(int));
+	if(put_user(len, optlen))
+		return -EFAULT;
+	if(copy_to_user(optval,&val,len))
+		return -EFAULT;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.26/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v2.1.26/linux/net/netrom/nr_dev.c	Tue Feb  4 06:44:26 1997
+++ linux/net/netrom/nr_dev.c	Tue Feb 25 17:12:51 1997
@@ -23,6 +23,8 @@
 
 #include <linux/config.h>
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#define __NO_VERSION__
+#include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -173,7 +175,7 @@
 {
 	dev->tbusy = 0;
 	dev->start = 1;
-
+	MOD_INC_USE_COUNT;
 	ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
 
 	return 0;
@@ -183,9 +185,8 @@
 {
 	dev->tbusy = 1;
 	dev->start = 0;
-
 	ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
-
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.26/linux/net/netrom/nr_route.c linux/net/netrom/nr_route.c
--- v2.1.26/linux/net/netrom/nr_route.c	Sun Feb  2 05:18:52 1997
+++ linux/net/netrom/nr_route.c	Tue Feb 25 17:12:51 1997
@@ -698,7 +698,7 @@
 
 	if (ax25 != NULL)
 		nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
-			    ax25->device, 0, sysctl_netrom_obsolescence_count_initialiser);
+			    ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser);
 
 	if ((dev = nr_dev_get(nr_dest)) != NULL)	/* Its for me */
 		return nr_rx_frame(skb, dev);
@@ -730,7 +730,7 @@
 	dptr  = skb_push(skb, 1);
 	*dptr = AX25_P_NETROM;
 
-	return ax25_send_frame(skb, 0, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+	return ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
 }
 
 int nr_nodes_get_info(char *buffer, char **start, off_t offset,
diff -u --recursive --new-file v2.1.26/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.26/linux/net/rose/af_rose.c	Sun Feb  2 05:18:52 1997
+++ linux/net/rose/af_rose.c	Tue Feb 25 17:12:51 1997
@@ -357,7 +357,7 @@
 		kfree_skb(skb, FREE_READ);
 	}
 
-	if (sk->wmem_alloc > 0 || sk->rmem_alloc > 0) {	/* Defer: outstanding buffers */
+	if (sk->wmem_alloc != 0 || sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
 		init_timer(&sk->timer);
 		sk->timer.expires  = jiffies + 10 * HZ;
 		sk->timer.function = rose_destroy_timer;
@@ -464,20 +464,18 @@
 	char *optval, int optlen)
 {
 	struct sock *sk = sock->sk;
-	int err, opt;
+	int opt;
 
 	if (level != SOL_ROSE)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
 
-	if (optval == NULL)
+	if(optlen<sizeof(int))
 		return -EINVAL;
+	if(get_user(opt, (int *)optval))
+		return -EFAULT;
 
-	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
-		return err;
-
-	get_user(opt, (int *)optval);
-
-	switch (optname) {
+	switch (optname) 
+	{
 		case ROSE_T1:
 			if (opt < 1)
 				return -EINVAL;
@@ -522,10 +520,13 @@
 {
 	struct sock *sk = sock->sk;
 	int val = 0;
-	int err; 
+	int len;
 
 	if (level != SOL_ROSE)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
+		
+	if(get_user(len,optlen))
+		return -EFAULT;
 	
 	switch (optname) {
 		case ROSE_T1:
@@ -556,16 +557,11 @@
 			return -ENOPROTOOPT;
 	}
 
-	if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
-		return err;
-
-	put_user(sizeof(int), (unsigned long *)optlen);
-
-	if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
-		return err;
-
-	put_user(val, (unsigned long *)optval);
-
+	len=min(len,sizeof(int));
+	if(put_user(len,optlen))
+		return -EFAULT;
+	if(copy_to_user(optval,&val,len))
+		return -EFAULT;
 	return 0;
 }
 
@@ -685,15 +681,6 @@
 			rose_destroy_socket(sk);
 			break;
 
-		case ROSE_STATE_1:
-			sk->protinfo.rose->state = ROSE_STATE_0;
-			sk->state                = TCP_CLOSE;
-			sk->shutdown            |= SEND_SHUTDOWN;
-			sk->state_change(sk);
-			sk->dead                 = 1;
-			rose_destroy_socket(sk);
-			break;
-
 		case ROSE_STATE_2:
 			sk->protinfo.rose->state = ROSE_STATE_0;
 			sk->state                = TCP_CLOSE;
@@ -703,6 +690,7 @@
 			rose_destroy_socket(sk);
 			break;			
 
+		case ROSE_STATE_1:
 		case ROSE_STATE_3:
 		case ROSE_STATE_4:
 			rose_clear_queues(sk);
diff -u --recursive --new-file v2.1.26/linux/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- v2.1.26/linux/net/rose/rose_dev.c	Tue Feb  4 06:44:27 1997
+++ linux/net/rose/rose_dev.c	Tue Feb 25 17:12:51 1997
@@ -19,6 +19,8 @@
 
 #include <linux/config.h>
 #if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#define __NO_VERSION__
+#include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -143,6 +145,7 @@
 
 static int rose_open(struct device *dev)
 {
+	MOD_INC_USE_COUNT;
 	dev->tbusy = 0;
 	dev->start = 1;
 
@@ -153,6 +156,7 @@
 
 static int rose_close(struct device *dev)
 {
+	MOD_DEC_USE_COUNT;
 	dev->tbusy = 1;
 	dev->start = 0;
 
diff -u --recursive --new-file v2.1.26/linux/net/rose/rose_in.c linux/net/rose/rose_in.c
--- v2.1.26/linux/net/rose/rose_in.c	Sun Feb  2 05:18:52 1997
+++ linux/net/rose/rose_in.c	Fri Feb 21 14:58:40 1997
@@ -106,6 +106,7 @@
 
 		case ROSE_CLEAR_REQUEST:
 			rose_clear_queues(sk);
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 			sk->protinfo.rose->state = ROSE_STATE_0;
 			sk->state                = TCP_CLOSE;
 			sk->err                  = ECONNREFUSED;
@@ -133,7 +134,9 @@
 	switch (frametype) {
 
 		case ROSE_CLEAR_REQUEST:
+			rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 		case ROSE_CLEAR_CONFIRMATION:
+			rose_clear_queues(sk);
 			sk->protinfo.rose->state = ROSE_STATE_0;
 			sk->state                = TCP_CLOSE;
 			sk->err                  = 0;
@@ -163,7 +166,6 @@
 	switch (frametype) {
 
 		case ROSE_RESET_REQUEST:
-			rose_clear_queues(sk);
 			rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
 			sk->protinfo.rose->condition = 0x00;
 			sk->protinfo.rose->timer     = 0;
@@ -270,8 +272,9 @@
 {
 	switch (frametype) {
 
-		case ROSE_RESET_CONFIRMATION:
 		case ROSE_RESET_REQUEST:
+			rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
+		case ROSE_RESET_CONFIRMATION:
 			sk->protinfo.rose->timer     = 0;
 			sk->protinfo.rose->condition = 0x00;
 			sk->protinfo.rose->va        = 0;
diff -u --recursive --new-file v2.1.26/linux/net/rose/rose_link.c linux/net/rose/rose_link.c
--- v2.1.26/linux/net/rose/rose_link.c	Sun Feb  2 05:18:52 1997
+++ linux/net/rose/rose_link.c	Tue Feb 25 17:12:51 1997
@@ -103,7 +103,7 @@
 	else
 		rose_call = &rose_callsign;
 
-	return ax25_send_frame(skb, 0, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+	return ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
 }
 
 /*
@@ -120,7 +120,7 @@
 	else
 		rose_call = &rose_callsign;
 
-	return ax25_link_up(rose_call, &neigh->callsign, neigh->dev);
+	return ax25_link_up(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
 }
 
 /*
diff -u --recursive --new-file v2.1.26/linux/net/rose/rose_route.c linux/net/rose/rose_route.c
--- v2.1.26/linux/net/rose/rose_route.c	Sun Feb  2 05:18:52 1997
+++ linux/net/rose/rose_route.c	Tue Feb 25 17:12:51 1997
@@ -556,7 +556,7 @@
 	dest_addr = (rose_address *)(skb->data + 4);
 
 	for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
-		if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->device == rose_neigh->dev)
+		if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev)
 			break;
 
 	if (rose_neigh == NULL)
diff -u --recursive --new-file v2.1.26/linux/net/socket.c linux/net/socket.c
--- v2.1.26/linux/net/socket.c	Fri Feb  7 05:57:06 1997
+++ linux/net/socket.c	Fri Feb 21 14:58:40 1997
@@ -38,6 +38,7 @@
  *		Alan Cox	:	Added thread locking to sys_* calls
  *					for sockets. May have errors at the
  *					moment.
+ *		Kevin Buhr	:	Fixed the dumb errors in the above.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -1222,7 +1223,7 @@
 	}
 	if (msg_sys.msg_iovlen>UIO_MAXIOV)
 	{
-		err=-EFAULT;
+		err=-EINVAL;
 		goto out;
 	}
 	
diff -u --recursive --new-file v2.1.26/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.26/linux/net/unix/af_unix.c	Sat Jan 25 13:46:14 1997
+++ linux/net/unix/af_unix.c	Tue Feb 25 17:12:51 1997
@@ -1433,8 +1433,8 @@
 	unix_ioctl,
 	unix_listen,
 	unix_shutdown,
-	NULL,
-	NULL,
+	sock_no_setsockopt,
+	sock_no_getsockopt,
 	sock_no_fcntl,
 	unix_stream_sendmsg,
 	unix_stream_recvmsg
@@ -1452,10 +1452,10 @@
 	unix_getname,
 	datagram_poll,
 	unix_ioctl,
-	NULL,
+	sock_no_listen,
 	unix_shutdown,
-	NULL,
-	NULL,
+	sock_no_setsockopt,
+	sock_no_getsockopt,
 	sock_no_fcntl,
 	unix_dgram_sendmsg,
 	unix_dgram_recvmsg
diff -u --recursive --new-file v2.1.26/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.26/linux/net/x25/af_x25.c	Sun Feb  2 05:18:53 1997
+++ linux/net/x25/af_x25.c	Fri Feb 21 14:58:40 1997
@@ -328,7 +328,7 @@
 		kfree_skb(skb, FREE_READ);
 	}
 
-	if (sk->wmem_alloc || sk->rmem_alloc) {	/* Defer: outstanding buffers */
+	if (sk->wmem_alloc != 0 || sk->rmem_alloc != 0) {	/* Defer: outstanding buffers */
 		init_timer(&sk->timer);
 		sk->timer.expires  = jiffies + 10 * HZ;
 		sk->timer.function = x25_destroy_timer;
@@ -355,21 +355,18 @@
 	int err, opt;
 
 	if (level != SOL_X25)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
 
-	if (optval == NULL)
-		return -EINVAL;
-
-	if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
-		return err;
-
-	get_user(opt, (int *)optval);
+	if(optlen<sizeof(int))
+		return-EINVAL;
+	if(get_user(opt, (int *)optval))
+		return -EFAULT;
 
-	switch (optname) {
+	switch (optname) 
+	{
 		case X25_QBITINCL:
 			sk->protinfo.x25->qbitincl = opt ? 1 : 0;
 			return 0;
-
 		default:
 			return -ENOPROTOOPT;
 	}
@@ -380,12 +377,16 @@
 {
 	struct sock *sk = sock->sk;
 	int val = 0;
-	int err; 
-
+	int len; 
+	
 	if (level != SOL_X25)
-		return -EOPNOTSUPP;
+		return -ENOPROTOOPT;
 
-	switch (optname) {
+	if(get_user(len,optlen))
+		return -EFAULT;
+		
+	switch (optname) 
+	{
 		case X25_QBITINCL:
 			val = sk->protinfo.x25->qbitincl;
 			break;
@@ -394,16 +395,11 @@
 			return -ENOPROTOOPT;
 	}
 
-	if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
-		return err;
-
-	put_user(sizeof(int), (unsigned long *)optlen);
-
-	if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
-		return err;
-
-	put_user(val, (unsigned long *)optval);
-
+	len=min(len,sizeof(int));
+	if(put_user(len, optlen))
+		return -EFAULT;
+	if(copy_to_user(optval,&val,len))
+		return -EFAULT;
 	return 0;
 }
 
@@ -548,15 +544,6 @@
 			x25_destroy_socket(sk);
 			break;
 
-		case X25_STATE_1:
-			sk->protinfo.x25->state = X25_STATE_0;
-			sk->state               = TCP_CLOSE;
-			sk->shutdown           |= SEND_SHUTDOWN;
-			sk->state_change(sk);
-			sk->dead                = 1;
-			x25_destroy_socket(sk);
-			break;
-
 		case X25_STATE_2:
 			sk->protinfo.x25->state = X25_STATE_0;
 			sk->state               = TCP_CLOSE;
@@ -566,6 +553,7 @@
 			x25_destroy_socket(sk);
 			break;			
 
+		case X25_STATE_1:
 		case X25_STATE_3:
 		case X25_STATE_4:
 			x25_clear_queues(sk);
diff -u --recursive --new-file v2.1.26/linux/net/x25/x25_in.c linux/net/x25/x25_in.c
--- v2.1.26/linux/net/x25/x25_in.c	Sun Jan 19 05:47:30 1997
+++ linux/net/x25/x25_in.c	Fri Feb 21 14:58:40 1997
@@ -117,6 +117,7 @@
 
 		case X25_CLEAR_REQUEST:
 			x25_clear_queues(sk);
+			x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
 			sk->protinfo.x25->state = X25_STATE_0;
 			sk->state               = TCP_CLOSE;
 			sk->err                 = ECONNREFUSED;
@@ -144,7 +145,9 @@
 	switch (frametype) {
 
 		case X25_CLEAR_REQUEST:
+			x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
 		case X25_CLEAR_CONFIRMATION:
+			x25_clear_queues(sk);
 			sk->protinfo.x25->state = X25_STATE_0;
 			sk->state               = TCP_CLOSE;
 			sk->err                 = 0;
@@ -177,7 +180,6 @@
 	switch (frametype) {
 
 		case X25_RESET_REQUEST:
-			x25_clear_queues(sk);
 			x25_write_internal(sk, X25_RESET_CONFIRMATION);
 			sk->protinfo.x25->condition = 0x00;
 			sk->protinfo.x25->timer     = 0;
@@ -306,8 +308,9 @@
 {
 	switch (frametype) {
 
-		case X25_RESET_CONFIRMATION:
 		case X25_RESET_REQUEST:
+			x25_write_internal(sk, X25_RESET_CONFIRMATION);
+		case X25_RESET_CONFIRMATION:
 			sk->protinfo.x25->timer     = 0;
 			sk->protinfo.x25->condition = 0x00;
 			sk->protinfo.x25->va        = 0;
diff -u --recursive --new-file v2.1.26/linux/scripts/Configure linux/scripts/Configure
--- v2.1.26/linux/scripts/Configure	Fri Feb  7 05:54:55 1997
+++ linux/scripts/Configure	Fri Feb 21 14:58:40 1997
@@ -1,3 +1,4 @@
+
 #! /bin/sh
 #
 # This script is used to configure the linux kernel.
@@ -56,6 +57,16 @@
 # Enable function cacheing.
 set -f -h
 
+# Newer shellutils have a more POSIX compliant behavior which is
+# sadly not backward compatible.
+if expr "$(expr --version)" : '.*1\.1[56]' > /dev/null; then
+   INT_FILTER='0$\|-\?[1-9][0-9]*$'
+   HEX_FILTER='[0-9a-fA-F]\+$'
+else
+   INT_FILTER='0$\|-?[1-9][0-9]*$'
+   HEX_FILTER='[0-9a-fA-F]+$'
+fi
+
 #
 # Dummy functions for use with a config.in modified for menuconf
 #
@@ -288,7 +299,7 @@
 	def=${old:-$3}
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
-	  if expr "$ans" : '0$\|-\?[1-9][0-9]*$' > /dev/null; then
+	  if expr "$ans" : $INT_FILTER > /dev/null; then
 	    define_int "$2" "$ans"
 	    break
 	  else
@@ -319,7 +330,7 @@
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
 	  ans=${ans#*[x,X]}
-	 if expr "$ans" : '[0-9a-fA-F]\+$' > /dev/null; then
+	 if expr "$ans" : $HEX_FILTER > /dev/null; then
 	   define_hex "$2" "$ans"
 	   break
 	 else
